Python 使用Bellman-Ford从源到负循环的最短路径
我想找到从图中的源节点到负循环的最短路径,而不必遍历任何循环两次如果对此有明确的答案,请回答。否则,我想展示我是如何处理这个问题的,希望其他人能告诉我哪里出了错。 下面是我的代码的简化版本,它实现了Bellman-Ford算法来检测负循环(使用)。这是该算法的简单实现。我进一步讨论我对它的修改Python 使用Bellman-Ford从源到负循环的最短路径,python,graph-theory,graph-algorithm,bellman-ford,Python,Graph Theory,Graph Algorithm,Bellman Ford,我想找到从图中的源节点到负循环的最短路径,而不必遍历任何循环两次如果对此有明确的答案,请回答。否则,我想展示我是如何处理这个问题的,希望其他人能告诉我哪里出了错。 下面是我的代码的简化版本,它实现了Bellman-Ford算法来检测负循环(使用)。这是该算法的简单实现。我进一步讨论我对它的修改 class NegativeWeightFinder: def __init__(self, graph: nx.Graph): self.graph = graph self.pre
class NegativeWeightFinder:
def __init__(self, graph: nx.Graph):
self.graph = graph
self.predecessor_to = {}
self.distance_to = {}
def initialize(self, source):
for node in self.graph:
# Initialize all distance_to values to infinity and all predecessor_to values to None
self.distance_to[node] = float('Inf')
self.predecessor_to[node] = None
# The distance from any node to (itself) == 0
self.distance_to[source] = 0
def bellman_ford(self, source):
self.initialize(source)
for i in range(len(self.graph) - 1):
for edge in self.graph.edges(data=True):
self.relax(edge)
for edge in self.graph.edges(data=True):
if self.distance_to[edge[0]] + edge[2]['weight'] <
self.distance_to[edge[1]]:
yield self._retrace_negative_loop(edge[1])
我想找出图中每个负循环的最小加权路径。我相信,在完成负循环之后
class NegativeWeightFinder:
def __init__(self, graph: nx.Graph):
self.graph = graph
self.predecessor_to = {}
self.distance_to = {}
self.predecessor_from = {}
self.distance_from = {}
self.seen_nodes = set()
def initialize(self, source):
for node in self.graph:
# Initialize all distance_to values to infinity and all predecessor_to values to None
self.distance_to[node] = float('Inf')
self.predecessor_to[node] = PrioritySet()
self.distance_from[node] = float('Inf')
self.predecessor_from[node] = PrioritySet()
# The distance from any node to (itself) == 0
self.distance_to[source] = 0
self.distance_from[source] = 0
def bellman_ford(self, source):
self.initialize(source)
# After len(graph) - 1 passes, algorithm is complete.
for i in range(len(self.graph) - 1):
# for each node in the graph, test if the distance to each of its siblings is shorter by going from
for edge in self.graph.edges(data=True):
self.relax(edge)
for edge in self.graph.edges(data=True):
if self.distance_to[edge[0]] + edge[2]['weight'] < self.distance_to[edge[1]]:
yield self._retrace_negative_loop(edge[1],
source=source)
def relax(self, edge):
if self.distance_to[edge[0]] + edge[2]['weight'] < self.distance_to[edge[1]]:
self.distance_to[edge[1]] = self.distance_to[edge[0]] + edge[2]['weight']
# no matter what, adds this edge to the PrioritySet in distance_to
self.predecessor_to[edge[1]].add(edge[0], self.distance_to[edge[0]] + edge[2]['weight'])
if self.distance_from[edge[1]] + edge[2]['weight'] < self.distance_from[edge[0]]:
self.distance_from[edge[0]] = self.distance_from[edge[1]] + edge[2]['weight']
self.predecessor_from[edge[0]].add(edge[1],
self.distance_from[edge[1]] + edge[2]['weight'])
return True
def _retrace_negative_loop(self, start, source=''):
arbitrage_loop = [start]
if source not in self.graph:
raise ValueError("source not in graph.")
# todo: i do not remember to which edge case this refers, test to see which then specify in the comment.
# adding the predecessor to start to arbitrage loop outside the while loop prevents an edge case.
next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1]
if unique_paths and next_node in self.seen_nodes:
raise SeenNodeError(next_node)
arbitrage_loop.insert(0, next_node)
# todo: refactor this so it is not while True, instead while not next_to_each_other
while True:
next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1]
# if this edge has been traversed over, negative cycle is complete.
if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]):
arbitrage_loop.insert(0, next_node)
arbitrage_loop = arbitrage_loop[:last_index_in_list(arbitrage_loop, next_node) + 1]
self.predecessor_to[arbitrage_loop[0]].pop()
def _pop_arbitrage_loop(loop, predecessor):
while predecessor[loop[0]].empty:
loop.pop(0)
# add the path from source -> min_distance_to_node to the beginning of arbitrage_loop
while arbitrage_loop[0] != source:
_pop_arbitrage_loop(arbitrage_loop, self.predecessor_to)
next_node = self.predecessor_to[arbitrage_loop[0]].pop()[1]
# if this edge has already been traversed over/ added to arbitrage_loop, must exit the cycle.
if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]):
self.predecessor_to[arbitrage_loop[0]].pop()
# this prevents an error where every edge from a node has been traversed over.
_pop_arbitrage_loop(arbitrage_loop, self.predecessor_to)
next_node = self.predecessor_to[arbitrage_loop[0]].pop()[1]
arbitrage_loop.insert(0, next_node)
# add the path from arbitrage_loop[-1] -> source to the end of arbitrage_loop
while arbitrage_loop[-1] != source:
next_node = self.predecessor_from[arbitrage_loop[-1]].peek()[1]
if next_to_each_other(arbitrage_loop, arbitrage_loop[-1], next_node):
self.predecessor_from[arbitrage_loop[-1]].pop()
arbitrage_loop.append(next_node)
self.reset_predecessor_iteration()
return arbitrage_loop
arbitrage_loop.insert(0, next_node)
def reset_predecessor_iteration(self):
for node in self.predecessor_to.keys():
self.predecessor_to[node].reset()
# predecessor_to and predecessor_to have the same keys
self.predecessor_from[node].reset()
def next_to_each_other(li: list, *args):
for i in range(len(li) - (len(args) - 1)):
for j in range(len(args)):
if li[i + j] != args[j]:
break
if j == len(args) - 1:
return True
return False
def last_index_in_list(li: list, element):
return len(li) - next(i for i, v in enumerate(reversed(li), 1) if v == element)
class NegativeWeightFinder:
定义初始化(self,图形:nx.graph):
self.graph=graph
self.u to={}
self.distance_to={}
self.preference_from={}
self.distance_from={}
self.seen_nodes=set()
def初始化(自身、源):
对于self.graph中的节点:
#将所有距离_值初始化为无穷大,将所有前置距离_值初始化为无
self.distance_到[node]=浮点('Inf')
self.preference_to[node]=PrioritySet()
self.distance_from[node]=float('Inf')
self.preference_from[node]=PrioritySet()
#从任何节点到(自身)的距离=0
到[源]的自距离=0
距离[源]的自距离=0
def bellman_ford(自身,来源):
自我初始化(源)
#len(graph)-1通过后,算法完成。
对于范围内的i(len(self.graph)-1):
#对于图中的每个节点,通过从
对于self.graph.edges中的边(data=True):
自我放松(边缘)
对于self.graph.edges中的边(data=True):
如果self.distance_到[edge[0]]+edge[2]['weight']min\u distance\u到\u节点的路径添加到套利\u循环的开头
而套利_循环[0]!=资料来源:
_pop_套利_循环(套利_循环,self.previous_to)
next_node=self.preducer_to[套利_循环[0]].pop()[1]
#如果此边已被穿过/添加到套利循环,则必须退出循环。
如果下一个到另一个(套利循环,下一个节点,套利循环[0]):
self.preducer_to[willidge_loop[0]].pop()
#这可以防止节点的每条边都已遍历的错误。
_pop_套利_循环(套利_循环,self.previous_to)
next_node=self.preducer_to[套利_循环[0]].pop()[1]
套利循环。插入(0,下一个节点)
#将套利_循环[-1]->source的路径添加到套利_循环的末尾
class PrioritySet:
def __init__(self):
self.heap = []
self.popped = {}
def add(self, d, pri):
heapq.heappush(self.heap, (pri, d))
return True
def pop(self):
popped = heapq.heappop(self.heap)
while popped[1] in self.popped.keys():
# Raises IndexError if done popping
try:
popped = heapq.heappop(self.heap)
# for debugging
except Exception as e:
raise e
self.popped[popped[1]] = popped[0]
return popped
def peek(self):
# self.heap[0][1] is the name of the element
try:
while self.heap[0][1] in self.popped.keys():
# Raises IndexError if done popping
heapq.heappop(self.heap)
# for debugging
except Exception as e:
raise e
return self.heap[0]
def reset(self):
for key, value in self.popped.items():
heapq.heappush(self.heap, (value, key))
self.popped = {}
@property
def empty(self):
for elem in self.heap:
if elem[1] not in self.popped.keys():
return False
return True
def __str__(self):
return str(list(self.heap))
def __repr__(self):
return str(self)
def __len__(self):
total = 0
seen = set()
for elem in self.heap:
if elem not in self.popped and elem not in seen:
total += 1
seen.add(elem)
return total
class NegativeWeightFinder:
def __init__(self, graph: nx.Graph):
self.graph = graph
self.predecessor_to = {}
self.distance_to = {}
self.predecessor_from = {}
self.distance_from = {}
self.seen_nodes = set()
def initialize(self, source):
for node in self.graph:
# Initialize all distance_to values to infinity and all predecessor_to values to None
self.distance_to[node] = float('Inf')
self.predecessor_to[node] = PrioritySet()
self.distance_from[node] = float('Inf')
self.predecessor_from[node] = PrioritySet()
# The distance from any node to (itself) == 0
self.distance_to[source] = 0
self.distance_from[source] = 0
def bellman_ford(self, source):
self.initialize(source)
# After len(graph) - 1 passes, algorithm is complete.
for i in range(len(self.graph) - 1):
# for each node in the graph, test if the distance to each of its siblings is shorter by going from
for edge in self.graph.edges(data=True):
self.relax(edge)
for edge in self.graph.edges(data=True):
if self.distance_to[edge[0]] + edge[2]['weight'] < self.distance_to[edge[1]]:
yield self._retrace_negative_loop(edge[1],
source=source)
def relax(self, edge):
if self.distance_to[edge[0]] + edge[2]['weight'] < self.distance_to[edge[1]]:
self.distance_to[edge[1]] = self.distance_to[edge[0]] + edge[2]['weight']
# no matter what, adds this edge to the PrioritySet in distance_to
self.predecessor_to[edge[1]].add(edge[0], self.distance_to[edge[0]] + edge[2]['weight'])
if self.distance_from[edge[1]] + edge[2]['weight'] < self.distance_from[edge[0]]:
self.distance_from[edge[0]] = self.distance_from[edge[1]] + edge[2]['weight']
self.predecessor_from[edge[0]].add(edge[1],
self.distance_from[edge[1]] + edge[2]['weight'])
return True
def _retrace_negative_loop(self, start, source=''):
arbitrage_loop = [start]
if source not in self.graph:
raise ValueError("source not in graph.")
# todo: i do not remember to which edge case this refers, test to see which then specify in the comment.
# adding the predecessor to start to arbitrage loop outside the while loop prevents an edge case.
next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1]
if unique_paths and next_node in self.seen_nodes:
raise SeenNodeError(next_node)
arbitrage_loop.insert(0, next_node)
# todo: refactor this so it is not while True, instead while not next_to_each_other
while True:
next_node = self.predecessor_to[arbitrage_loop[0]].peek()[1]
# if this edge has been traversed over, negative cycle is complete.
if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]):
arbitrage_loop.insert(0, next_node)
arbitrage_loop = arbitrage_loop[:last_index_in_list(arbitrage_loop, next_node) + 1]
self.predecessor_to[arbitrage_loop[0]].pop()
def _pop_arbitrage_loop(loop, predecessor):
while predecessor[loop[0]].empty:
loop.pop(0)
# add the path from source -> min_distance_to_node to the beginning of arbitrage_loop
while arbitrage_loop[0] != source:
_pop_arbitrage_loop(arbitrage_loop, self.predecessor_to)
next_node = self.predecessor_to[arbitrage_loop[0]].pop()[1]
# if this edge has already been traversed over/ added to arbitrage_loop, must exit the cycle.
if next_to_each_other(arbitrage_loop, next_node, arbitrage_loop[0]):
self.predecessor_to[arbitrage_loop[0]].pop()
# this prevents an error where every edge from a node has been traversed over.
_pop_arbitrage_loop(arbitrage_loop, self.predecessor_to)
next_node = self.predecessor_to[arbitrage_loop[0]].pop()[1]
arbitrage_loop.insert(0, next_node)
# add the path from arbitrage_loop[-1] -> source to the end of arbitrage_loop
while arbitrage_loop[-1] != source:
next_node = self.predecessor_from[arbitrage_loop[-1]].peek()[1]
if next_to_each_other(arbitrage_loop, arbitrage_loop[-1], next_node):
self.predecessor_from[arbitrage_loop[-1]].pop()
arbitrage_loop.append(next_node)
self.reset_predecessor_iteration()
return arbitrage_loop
arbitrage_loop.insert(0, next_node)
def reset_predecessor_iteration(self):
for node in self.predecessor_to.keys():
self.predecessor_to[node].reset()
# predecessor_to and predecessor_to have the same keys
self.predecessor_from[node].reset()
def next_to_each_other(li: list, *args):
for i in range(len(li) - (len(args) - 1)):
for j in range(len(args)):
if li[i + j] != args[j]:
break
if j == len(args) - 1:
return True
return False
def last_index_in_list(li: list, element):
return len(li) - next(i for i, v in enumerate(reversed(li), 1) if v == element)