Para este algoritmo:
Grafo
é uma classe que representa um grafoStack
é uma estrutura de pilha
append
e pop
start
e goal
são “nomes” de vérticesvisited(vertice)
retorna True
se o vértice já foi visitado.set_visited(vertice)
marca o vértice como visitado.
set
(ou dict
se a lista guardar mais informações).processa_caminho(v)
até o vértice v
.
from
como anterior de `v.def DFS(Grafo, start, goal):
assert(start in Grafo.vertices)
assert(goal in Grafo.vertices)
S = Stack()
S.push(start)
while not S.is_empty():
v = S.pop()
if goal == v:
return # encontrou o caminho
if not visited(v):
# processa o caminho "até v"
processa_caminho(v)
set_visited(v)
for u in Grafo.neighbors(v):
S.push(u)
Grafo
é uma classe que representa um grafoQueue
é uma estrutura de fila
queue.deque
e os métodos appendleft
e pop
.start
e goal
são “nomes” de vérticesvisited(vertice)
retorna True
se o vértice já foi visitado.set_visited(vertice)
marca o vértice como visitado.
set
(ou dict
se a lista guardar mais informações).processa_caminho(v)
processa o caminho até o vértice v
.
from
como anterior de v
.def BFS(Grafo, start, goal):
assert(start in Grafo.vertices)
assert(goal in Grafo.vertices)
Q = Queue()
Q.enqueue(start)
while not Q.is_empty():
v = Q.dequeue()
if goal == v:
return # encontrou o caminho
if not visited(v):
# processa o caminho até v
processa_caminho(v)
set_visited(v)
for u in Grafo.neighbors(v):
Q.enqueue(u)
O algoritmo Branch and Bound leva em consideração o caminho até o momento e se este caminho não pode levar a um resultado melhor, não segue criando divisões a partir daquele vértice.
def branch_and_bound(Grafo, start, goal):
assert(start in Grafo.vertices)
assert(goal in Grafo.vertices)
best_so_far = (None, +INF)
Q = Queue()
Q.enqueue(start)
while not Q.is_empty():
v = Q.dequeue()
if goal == v:
# 'candidate' é o comprimento atual do caminho
new_path, candidate = processa_caminho(v)
path, limit = best_so_far
if candidate < limit:
best_so_far = (new_path, candidate)
else:
ajusta_caminho(start, v)
for u in Grafo.neighbors(v):
# Só adiciona caminhos que podem ser melhores
if caminho_ate_aqui(start, u) < best_so_far:
Q.enqueue(u)
Para o algoritmo $A^*$, será necessária a criação de uma heurística admissível, ou, melhor ainda, uma heurística consistente.
Sobre o algoritmo $A^*$:
frontier = PriorityQueue()
frontier.put(start, 0)
came_from = {}
cost_so_far = {}
came_from[start] = None
cost_so_far[start] = 0
def heuristic(a, b):
"""Manhattan distance on a square grid."""
return abs(a.x - b.x) + abs(a.y - b.y)
while not frontier.empty():
current = frontier.get()
if current == goal:
break
for next in graph.neighbors(current):
new_cost = cost_so_far[current] + graph.cost(current, next)
if next not in cost_so_far or new_cost < cost_so_far[next]:
cost_so_far[next] = new_cost
priority = new_cost + heuristic(goal, next)
frontier.put(next, priority)
came_from[next] = current
O pseudocódigo para a implementação do algoritmo de Dijkstra:
function Dijkstra(Graph, source, goal):
for each vertex v in Graph.Vertices:
dist[v] = INFINITY
prev[v] = UNDEFINED
add v to Q
dist[source] = 0
while Q is not empty:
u = vertex in Q with min dist[u]
remove u from Q
for each neighbor v of u still in Q:
alt = dist[u] + Graph.edge(u, v)
if alt < dist[v]:
dist[v] = alt
prev[v] = u
return dist[], prev[]
Para uma implementação eficiente do algoritmo, deve ser utilizada uma fila de prioridades (priority queue), que pode ser implementada como um heap mínimo.