use*_*080 9 python algorithm dijkstra
我试图使用数组在python中实现Dijkstra的算法.这是我的实施.
def extract(Q, w):
m=0
minimum=w[0]
for i in range(len(w)):
if w[i]<minimum:
minimum=w[i]
m=i
return m, Q[m]
def dijkstra(G, s, t='B'):
Q=[s]
p={s:None}
w=[0]
d={}
for i in G:
d[i]=float('inf')
Q.append(i)
w.append(d[i])
d[s]=0
S=[]
n=len(Q)
while Q:
u=extract(Q,w)[1]
S.append(u)
#w.remove(extract(Q, d, w)[0])
Q.remove(u)
for v in G[u]:
if d[v]>=d[u]+G[u][v]:
d[v]=d[u]+G[u][v]
p[v]=u
return d, p
B='B'
A='A'
D='D'
G='G'
E='E'
C='C'
F='F'
G={B:{A:5, D:1, G:2}, A:{B:5, D:3, E:12, F:5}, D:{B:1, G:1, E:1, A:3}, G:{B:2, D:1, C:2}, C:{G:2, E:1, F:16}, E:{A:12, D:1, C:1, F:2}, F:{A:5, E:2, C:16}}
print "Assuming the start vertex to be B:"
print "Shortest distances", dijkstra(G, B)[0]
print "Parents", dijkstra(G, B)[1]
Run Code Online (Sandbox Code Playgroud)
我希望答案是:
Assuming the start vertex to be B:
Shortest distances {'A': 4, 'C': 4, 'B': 0, 'E': 2, 'D': 1, 'G': 2, 'F': 4}
Parents {'A': 'D', 'C': 'G', 'B': None, 'E': 'D', 'D': 'B', 'G': 'D', 'F': 'E'}
Run Code Online (Sandbox Code Playgroud)
但是,我得到的答案是这样的:
Assuming the start vertex to be B:
Shortest distances {'A': 4, 'C': 4, 'B': 0, 'E': 2, 'D': 1, 'G': 2, 'F': 10}
Parents {'A': 'D', 'C': 'G', 'B': None, 'E': 'D', 'D': 'B', 'G': 'D', 'F': 'A'}.
Run Code Online (Sandbox Code Playgroud)
对于节点F,程序给出错误的答案.有人可以告诉我为什么吗?
Hyp*_*eus 27
正如其他人所指出的那样,由于不使用可理解的变量名,几乎不可能调试代码.
在关于Dijkstra算法的wiki文章之后,可以按照这些方式(以及其他一百万种方式)实现它:
nodes = ('A', 'B', 'C', 'D', 'E', 'F', 'G')
distances = {
'B': {'A': 5, 'D': 1, 'G': 2},
'A': {'B': 5, 'D': 3, 'E': 12, 'F' :5},
'D': {'B': 1, 'G': 1, 'E': 1, 'A': 3},
'G': {'B': 2, 'D': 1, 'C': 2},
'C': {'G': 2, 'E': 1, 'F': 16},
'E': {'A': 12, 'D': 1, 'C': 1, 'F': 2},
'F': {'A': 5, 'E': 2, 'C': 16}}
unvisited = {node: None for node in nodes} #using None as +inf
visited = {}
current = 'B'
currentDistance = 0
unvisited[current] = currentDistance
while True:
for neighbour, distance in distances[current].items():
if neighbour not in unvisited: continue
newDistance = currentDistance + distance
if unvisited[neighbour] is None or unvisited[neighbour] > newDistance:
unvisited[neighbour] = newDistance
visited[current] = currentDistance
del unvisited[current]
if not unvisited: break
candidates = [node for node in unvisited.items() if node[1]]
current, currentDistance = sorted(candidates, key = lambda x: x[1])[0]
print(visited)
Run Code Online (Sandbox Code Playgroud)
这段代码比必要的更加冗长,我希望将您的代码与我的代码进行比较,您可能会发现一些差异.
结果是:
{'E': 2, 'D': 1, 'G': 2, 'F': 4, 'A': 4, 'C': 3, 'B': 0}
Run Code Online (Sandbox Code Playgroud)
Enz*_*ama 10
此实现仅使用数组和堆 ds。
import heapq as hq
import math
def dijkstra(G, s):
n = len(G)
visited = [False]*n
weights = [math.inf]*n
path = [None]*n
queue = []
weights[s] = 0
hq.heappush(queue, (0, s))
while len(queue) > 0:
g, u = hq.heappop(queue)
visited[u] = True
for v, w in G[u]:
if not visited[v]:
f = g + w
if f < weights[v]:
weights[v] = f
path[v] = u
hq.heappush(queue, (f, v))
return path, weights
G = [[(1, 6), (3, 7)],
[(2, 5), (3, 8), (4, -4)],
[(1, -2), (4, 7)],
[(2, -3), (4, 9)],
[(0, 2)]]
print(dijkstra(G, 0))
Run Code Online (Sandbox Code Playgroud)
我希望这可以帮助某人,这有点晚了。
Ion*_*cus 10
我需要一个也能返回路径的解决方案,因此我使用有关 Dijkstra 的多个问题/答案的想法组合了一个简单的类:
class Dijkstra:
def __init__(self, vertices, graph):
self.vertices = vertices # ("A", "B", "C" ...)
self.graph = graph # {"A": {"B": 1}, "B": {"A": 3, "C": 5} ...}
def find_route(self, start, end):
unvisited = {n: float("inf") for n in self.vertices}
unvisited[start] = 0 # set start vertex to 0
visited = {} # list of all visited nodes
parents = {} # predecessors
while unvisited:
min_vertex = min(unvisited, key=unvisited.get) # get smallest distance
for neighbour, _ in self.graph.get(min_vertex, {}).items():
if neighbour in visited:
continue
new_distance = unvisited[min_vertex] + self.graph[min_vertex].get(neighbour, float("inf"))
if new_distance < unvisited[neighbour]:
unvisited[neighbour] = new_distance
parents[neighbour] = min_vertex
visited[min_vertex] = unvisited[min_vertex]
unvisited.pop(min_vertex)
if min_vertex == end:
break
return parents, visited
@staticmethod
def generate_path(parents, start, end):
path = [end]
while True:
key = parents[path[0]]
path.insert(0, key)
if key == start:
break
return path
Run Code Online (Sandbox Code Playgroud)
示例图和用法(绘图是使用这个漂亮的工具生成的):

input_vertices = ("A", "B", "C", "D", "E", "F", "G")
input_graph = {
"A": {"B": 5, "D": 3, "E": 12, "F": 5},
"B": {"A": 5, "D": 1, "G": 2},
"C": {"E": 1, "F": 16, "G": 2},
"D": {"A": 3, "B": 1, "E": 1, "G": 1},
"E": {"A": 12, "C": 1, "D": 1, "F": 2},
"F": {"A": 5, "C": 16, "E": 2},
"G": {"B": 2, "C": 2, "D": 1}
}
start_vertex = "B"
end_vertex= "C"
dijkstra = Dijkstra(input_vertices, input_graph)
p, v = dijkstra.find_route(start_vertex, end_vertex)
print("Distance from %s to %s is: %.2f" % (start_vertex, end_vertex, v[end_vertex]))
se = dijkstra.generate_path(p, start_vertex, end_vertex)
print("Path from %s to %s is: %s" % (start_vertex, end_vertex, " -> ".join(se)))
Run Code Online (Sandbox Code Playgroud)
输出
Distance from B to C is: 3.00
Path from B to C is: B -> D -> E -> C
Run Code Online (Sandbox Code Playgroud)
我用更详细的形式写了它,以使新手读者更清楚:
from collections import defaultdict
def get_shortest_path(weighted_graph, start, end):
"""
Calculate the shortest path for a directed weighted graph.
Node can be virtually any hashable datatype.
:param start: starting node
:param end: ending node
:param weighted_graph: {"node1": {"node2": "weight", ...}, ...}
:return: ["START", ... nodes between ..., "END"] or None, if there is no
path
"""
# We always need to visit the start
nodes_to_visit = {start}
visited_nodes = set()
distance_from_start = defaultdict(lambda: float("inf"))
# Distance from start to start is 0
distance_from_start[start] = 0
tentative_parents = {}
while nodes_to_visit:
# The next node should be the one with the smallest weight
current = min(
[(distance_from_start[node], node) for node in nodes_to_visit]
)[1]
# The end was reached
if current == end:
break
nodes_to_visit.discard(current)
visited_nodes.add(current)
for neighbour, distance in weighted_graph[current]:
if neighbour in visited_nodes:
continue
neighbour_distance = distance_from_start[current] + distance
if neighbour_distance < distance_from_start[neighbour]:
distance_from_start[neighbour] = neighbour_distance
tentative_parents[neighbour] = current
nodes_to_visit.add(neighbour)
return _deconstruct_path(tentative_parents, end)
def _deconstruct_path(tentative_parents, end):
if end not in tentative_parents:
return None
cursor = end
path = []
while cursor:
path.append(cursor)
cursor = tentative_parents.get(cursor)
return list(reversed(path))
Run Code Online (Sandbox Code Playgroud)
小智 8
这不是我的答案 - 我的教授比我的尝试更有效率.这是他的方法,显然使用辅助函数来完成重复性任务
def dijkstra(graph, source):
vertices, edges = graph
dist = dict()
previous = dict()
for vertex in vertices:
dist[vertex] = float("inf")
previous[vertex] = None
dist[source] = 0
Q = set(vertices)
while len(Q) > 0:
u = minimum_distance(dist, Q)
print('Currently considering', u, 'with a distance of', dist[u])
Q.remove(u)
if dist[u] == float('inf'):
break
n = get_neighbours(graph, u)
for vertex in n:
alt = dist[u] + dist_between(graph, u, vertex)
if alt < dist[vertex]:
dist[vertex] = alt
previous[vertex] = u
return previous
Run Code Online (Sandbox Code Playgroud)
给出一个图表
({'A','B','C','D'},{('A','B',5),('B','A',5),('B',' C',10),('B','D',6),('C','D',2),('D','C',2)})
命令print(dijkstra(graph, 'A')产生
目前考虑A距离为0
目前考虑B距离为5
目前考虑D距离为11
目前考虑C距离为13
id est:
{'C':'D','D':'B','A':无,'B':'A'} =>按随机顺序排列
基于 CLRS 2nd Ed 的实现。第 24.3 章
d是增量,p是前辈
import heapq
def dijkstra(g, s, t):
q = []
d = {k: sys.maxint for k in g.keys()}
p = {}
d[s] = 0
heapq.heappush(q, (0, s))
while q:
last_w, curr_v = heapq.heappop(q)
for n, n_w in g[curr_v]:
cand_w = last_w + n_w # equivalent to d[curr_v] + n_w
# print d # uncomment to see how deltas are updated
if cand_w < d[n]:
d[n] = cand_w
p[n] = curr_v
heapq.heappush(q, (cand_w, n))
print "predecessors: ", p
print "delta: ", d
return d[t]
def test():
og = {}
og["s"] = [("t", 10), ("y", 5)]
og["t"] = [("y", 2), ("x", 1)]
og["y"] = [("t", 3), ("x", 9), ("z", 2)]
og["z"] = [("x", 6), ("s", 7)]
og["x"] = [("z", 4)]
assert dijkstra(og, "s", "x") == 9
if __name__ == "__main__":
test()
Run Code Online (Sandbox Code Playgroud)
实现假设所有节点都表示为键。如果说节点(例如上面示例中的“x”)未定义为og 中的键,则deltas d将缺少该键并检查cand_w < d[n]是否无法正常工作。