Advertisement
Guest User

Untitled

a guest
Dec 5th, 2016
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.95 KB | None | 0 0
  1. import random
  2. import heapq
  3. import math
  4. from time import time
  5. from collections import deque
  6.  
  7. visitar_nulo = lambda a,b,c,d: True
  8. heuristica_nula = lambda actual,destino: 0
  9.  
  10. class Grafo(object):
  11.     '''Clase que representa un grafo. El grafo puede ser dirigido, o no, y puede
  12.       no indicarsele peso a las aristas (se comportara como peso = 1).
  13.      Implementado como "diccionario de diccionarios"
  14.    '''
  15.    
  16.     def __init__(self, es_dirigido = False):
  17.         '''Crea el grafo. El parametro 'es_dirigido' indica si sera dirigido, o no.'''
  18.         self.es_dirigido = es_dirigido
  19.         self.cant_vertices = 0
  20.         self.vertices = {}
  21.         self.personajes = {}
  22.         self.nodos = {}
  23.         self.cant_aristas = 0
  24.    
  25.     def __iter__(self):
  26.         '''Devuelve un iterador para el grafo'''
  27.         return iter(self.vertices)
  28.    
  29.     def __len__(self):
  30.         '''Devuelve la cantidad de vertices del grafo'''
  31.         return self.cant_vertices
  32.        
  33.     def keys(self):
  34.         '''Devuelve una lista de identificadores de vertices. Iterar sobre
  35.           ellos es equivalente a iterar sobre el grafo.
  36.        '''
  37.         return self.vertices.keys()
  38.  
  39.     def __setitem__(self, id, valor):
  40.         '''Agrega un nuevo vertice con el par <id, valor> indicado. ID debe
  41.           ser de identificador unico del vertice. En caso que el identificador
  42.           ya se encuentre asociado a un vertice, se actualizara el valor.
  43.        '''
  44.        
  45.         if not id in self.nodos:
  46.             self.vertices[id] = {}
  47.             self.personajes[valor] = id
  48.             self.cant_vertices += 1
  49.             self.nodos[id] = Nodo_Grafo(id, valor)
  50.         self.nodos[id].nombre = valor
  51.        
  52.        
  53.     def __delitem__(self, id):
  54.         '''Elimina el vertice del grafo. Si no existe el identificador en el grafo,
  55.           lanzara KeyError.
  56.           Borra tambien todas las aristas que salian y entraban al vertice en cuestion.
  57.        '''
  58.        
  59.         if not id in self.vertices:
  60.             raise KeyError("No se encontro la clave")
  61.         for i in self.vertices:
  62.             if id in self.vertices[i]:
  63.                 self.vertices[i].pop(id)
  64.         self.vertices.pop(id)
  65.         self.nodos.pop(id)
  66.  
  67.     def __contains__(self, id):
  68.         ''' Determina si el grafo contiene un vertice con el identificador indicado.'''
  69.         return id in self.vertices.keys()
  70.    
  71.     def cantidad_aristas(self):
  72.         return self.cant_aristas
  73.        
  74.        
  75.     def agregar_arista(self, desde, hasta, peso = 1):
  76.         '''Agrega una arista que conecta los vertices indicados.
  77.           Parametros:
  78.            * desde y hasta: identificadores de vertices dentro del grafo. Si alguno
  79.              de estos no existe dentro del grafo, lanzara KeyError.
  80.            * Peso: valor de peso que toma la conexion. Si no se indica, valdra 1.
  81.            Si el grafo es no-dirigido, tambien agregara la arista reciproca.
  82.        '''
  83.         if not desde or not hasta in self.vertices:
  84.             raise KeyError("No se encontro uno de los vertices")
  85.         self.cant_aristas += 1
  86.         self.vertices[desde][hasta] = peso
  87.         if self.es_dirigido:
  88.             self.vertices[hasta][desde] = peso
  89.  
  90.        
  91.     def borrar_arista(self, desde, hasta):
  92.         '''Borra una arista que conecta los vertices indicados.
  93.           Parametros:
  94.            * desde y hasta: identificadores de vertices dentro del grafo.
  95.              Si alguno de estos no existe dentro del grafo, lanzara KeyError.
  96.           En caso de no existir la arista, se lanzara ValueError.
  97.        '''
  98.         if not desde or not hasta in self.vertices:
  99.             raise KeyError("No se encontro uno de los vertices")
  100.         if not hasta in self.vertices[desde][1]:
  101.             raise ValueError("No existe la arista")
  102.         if not self.es_dirigido:
  103.             self.vertices[hasta].pop(desde)
  104.         self.vertices[desde].pop(hasta)
  105.    
  106.     def obtener_peso_arista(self, desde, hasta):
  107.         '''Obtiene el peso de la arista que va desde el vertice 'desde', hasta el vertice 'hasta'.
  108.           Parametros:
  109.            * desde y hasta: identificadores de vertices dentro del grafo.
  110.              Si alguno de estos no existe dentro del grafo, lanzara KeyError.
  111.           En caso de no existir la union consultada, se devuelve None.
  112.        '''
  113.         if not desde or not hasta in self.vertices:
  114.             raise KeyError("No se encontro uno de los vertices")
  115.         if not hasta in self.vertices[desde]: return None
  116.         return self.vertices[desde][hasta]
  117.    
  118.     def adyacentes(self, id):
  119.         '''Devuelve una lista con los vertices (identificadores) adyacentes
  120.           al indicado. Si no existe el vertice, se lanzara KeyError
  121.        '''
  122.         if not id in self.vertices:
  123.             raise KeyError("No se encontro el vertice")
  124.         return list(self.vertices[id].keys())
  125.        
  126.     def obtener_personaje(self, id):
  127.         '''Devuelve el valor de la id'''
  128.         if not id in self.nodos:
  129.             raise KeyError("No se encontro la id")
  130.         return self.nodos[id].nombre
  131.        
  132.     def grado(self):
  133.         '''Devuelve el grado total del grafo'''
  134.         grado_total = 0
  135.         for vertice in self.vertices:
  136.             for adyacente in Grafo.adyacentes(self, vertice):
  137.                 grado_total += 1
  138.         return grado_total
  139.        
  140.     def obtener_id(self, valor):
  141.         '''Devuelve la ide del valor'''
  142.         if not valor in self.personajes:
  143.             raise KeyError("La No se encontro el nombre")
  144.         return self.personajes[valor]
  145.    
  146.                    
  147.     def random_walk(self, largo, origen = None, pesado = False):
  148.         ''' Devuelve una lista con un recorrido aleatorio de grafo.
  149.            Parametros:
  150.                * largo: El largo del recorrido a realizar
  151.                * origen: Vertice (id) por el que se debe comenzar el recorrido.
  152.                  Si origen = None, se comenzara por un vertice al azar.
  153.                * pesado: indica si se tienen en cuenta los pesos de las aristas para determinar
  154.                  las probabilidades de movernos de un vertice a uno de sus vecinos (False = todo equiprobable).
  155.            Devuelve:
  156.                Una lista con los vertices (ids) recorridos, en el orden del recorrido.
  157.        '''
  158.         if not self.vertices:
  159.             raise ValueError("No hay vertices en el grafo")
  160.         recorrido = []
  161.         cont_recorrido = 0
  162.         if origen:
  163.             if not origen in self.vertices:
  164.                 raise KeyError("No se encontro el vertice origen")
  165.             recorrido.append(origen)
  166.         else:
  167.             lista_claves = list(self.vertices.keys())
  168.             vertice_random = random.choice(lista_claves)
  169.             while not vertice_random:
  170.                 vertice_random = random.choice(lista_claves)
  171.             recorrido.append(vertice_random)
  172.        
  173.         vertice_partida = recorrido[cont_recorrido]
  174.        
  175.         while cont_recorrido < largo:
  176.             if not vertice_partida: break
  177.             diccionario_adyacentes = self.vertices[vertice_partida]
  178.             if pesado:
  179.                 vertice_random = vertice_random_por_peso(diccionario_adyacentes)
  180.             else:
  181.                 lista_claves = list(self.vertices[vertice_partida].keys())
  182.                 vertice_random = random.choice(lista_claves)
  183.                
  184.             recorrido.append(vertice_random)
  185.             cont_recorrido+= 1
  186.             vertice_partida = recorrido[cont_recorrido]
  187.            
  188.            
  189.         return recorrido
  190.            
  191.     def dijkstra(self, origen, destino, minimo = True):
  192.    
  193.         '''Devuelve el recorrido dependiendo si es maximo o minimo desde el
  194.            origen hasta el destino, aplicando el algoritmo de Dijkstra.
  195.           Parametros:
  196.            * minimo: indica si el camino devuelto sera el minimo si minimo es True
  197.              devolvera el maximo si es False
  198.            * origen y destino: identificadores de vertices dentro del grafo.
  199.              Si alguno de estos no existe dentro del grafo, lanzara KeyError.
  200.           Devuelve:
  201.            * Listado de vertices (identificadores) ordenado con el recorrido, incluyendo
  202.              a los vertices de origen y destino. En caso que no exista camino entre
  203.              el origen y el destino, se devuelve None.
  204.        '''
  205.         heap = []
  206.         camino = []
  207.         for vertice in self.vertices:
  208.             nodo = self.nodos[vertice]
  209.             nodo.visitado = False
  210.             nodo.distancia = math.inf
  211.             if vertice == origen:
  212.                 heapq.heappush(heap,nodo)
  213.                 nodo.distancia = 0
  214.         while heap:
  215.             nodo = heapq.heappop(heap)
  216.             nodo.visitado = True
  217.             vertice = nodo.vertice
  218.             dist = nodo.distancia
  219.             for adyacente in self.vertices[vertice]:
  220.                 if not adyacente: continue
  221.                 peso = self.vertices[vertice][adyacente]
  222.                 nodo_ad = self.nodos[adyacente]
  223.                 if not nodo_ad.visitado and nodo_ad.distancia > (dist + peso):
  224.                    
  225.                     if minimo:
  226.                         nodo_ad.cambiar_dist(dist + peso)
  227.                     else:
  228.                         nodo_ad.cambiar_dist(dist + 1/peso)
  229.                     nodo_ad.cambiar_padre(vertice)
  230.                     heapq.heappush(heap,nodo_ad)
  231.        
  232.         vertice = destino
  233.         camino.append(vertice)
  234.         while vertice != origen:
  235.             padre = self.nodos[vertice].padre
  236.             camino.append(padre)
  237.             vertice = padre
  238.         return camino
  239.        
  240.        
  241.    
  242.     def bfs(self, inicio = None, visitar = None, extra = None):
  243.  
  244.         '''Realiza un recorrido BFS dentro del grafo
  245.        Parametros:
  246.            - inicio: identificador del vertice que se usa como inicio. Si se indica un vertice, el recorrido se comenzara en dicho vertice,
  247.            y solo se seguira hasta donde se pueda (no se seguira con los vertices que falten visitar)
  248.        Salida:
  249.            Tupla (padres, orden), donde :
  250.                - 'padres' es un diccionario que indica para un identificador, cual es el identificador del vertice padre en el recorrido BFS (None si es el inicio)
  251.                - 'orden' es un diccionario que indica para un identificador, cual es su orden en el recorrido BFS
  252.        '''
  253.         padres = {}
  254.         orden = {}
  255.        
  256.         for nodo in self.nodos:
  257.             nodo = self.nodos[nodo]
  258.             nodo.visitado = False
  259.             padres[nodo.vertice] = None
  260.             orden[nodo.vertice] = math.inf
  261.        
  262.         if inicio:
  263.             nodo = self.nodos[inicio]
  264.             vertice = inicio
  265.         else:
  266.             nodos = list(self.nodos.keys())
  267.             vertice = random.choice(nodos)
  268.             nodo = self.nodos[vertice]
  269.         vertice_origen = vertice
  270.         padres[vertice] = None
  271.         nodo.visitado = True
  272.         orden[vertice] = 0
  273.         cola = deque([])
  274.         cola.append(nodo)
  275.         while cola:
  276.             nodo = cola.popleft()
  277.             vertice = nodo.vertice
  278.             if visitar and not visitar(nodo,extra): break
  279.             for adyacente in self.vertices[vertice]:
  280.                 nodo_ad = self.nodos[adyacente]
  281.                 if nodo_ad.visitado == False:
  282.                     nodo_ad.visitado = True
  283.                     cola.append(nodo_ad)
  284.                     orden[adyacente] = orden[vertice] + 1
  285.                     padres[adyacente] = vertice
  286.    
  287.         return (orden, padres, vertice_origen)  
  288.    
  289.            
  290.        
  291. class Nodo_Grafo():
  292.    
  293.     def __init__(self, vertice, nombre, distancia = 0, visitado = False, padre = None):
  294.         self.vertice = vertice
  295.         self.distancia = distancia
  296.         self.visitado = visitado
  297.         self.padre = padre
  298.         self.nombre = nombre
  299.         self.label_act = int(vertice)
  300.        
  301.        
  302.     def visitado(self):
  303.         return self.visitado
  304.        
  305.     def cambiar_dist(self, distancia_nueva):
  306.         self.distancia = distancia_nueva
  307.  
  308.     def cambiar_padre(self, padre_nuevo):
  309.         self.padre = padre_nuevo
  310.        
  311.     def __lt__(self,otro):
  312.         return self.distancia < otro.distancia
  313.    
  314.     def __gt__(self,otro):
  315.         return self.distancia > otro.distancia
  316.        
  317.     def __eq__(self,otro):
  318.         return self.distancia == otro.distancia
  319.        
  320.     def __str__(self):
  321.         return str(self.vertice)+"; "+str(self.distancia)
  322.        
  323.        
  324.        
  325. def vertice_random_por_peso(diccionario_adyacentes):
  326.     '''Devuelve un vertice adyacente random, con altas prioridades de que su peso sea mayor al de los demas'''
  327.    
  328.     total = sum(diccionario_adyacentes.values())
  329.     rand = random.uniform(0, total)
  330.     acum = 0
  331.     for vertice, peso_arista in diccionario_adyacentes.items():
  332.         if acum + peso_arista >= rand:
  333.             return vertice
  334.         acum += peso_arista
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement