Advertisement
Guest User

Untitled

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