我正在研究Dijkstras算法的实现,以检索路由网络上互连节点之间的最短路径。我正在努力。当我将起始节点传递到算法中时,它将返回所有节点的所有最短路径。
我的问题:如何检索从节点A到节点G的所有可能路径,甚至从节点A到节点A的所有可能路径。
我正在研究Dijkstras算法的实现,以检索路由网络上互连节点之间的最短路径。我正在努力。当我将起始节点传递到算法中时,它将返回所有节点的所有最短路径。
我的问题:如何检索从节点A到节点G的所有可能路径,甚至从节点A到节点A的所有可能路径。
Answers:
visited
集合(如原始算法所建议的那样),否则您将仅获得部分路径。另外,您应该将路径限制为一定长度,以避免无限循环[如果图形具有循环...]。祝好运!
k
最短简单路径的多项式时间(非伪多项式)算法。由于最多有(3/2)n!
这样的路径,因此您可以进行二进制搜索并查找是否存在一个简单的length路径n
。由于log{(3/2)n!}
是中的多项式n
,因此编码数量和所需的重复次数都是输入大小的多项式。由于该算法也为输入运行多项式时间,因此总运行时间为多项式,答案是节点之间是否存在哈密顿路径。因此,如果存在这种算法,则P = NP。
(3/2)n!
。
我已经实现了一个版本,该版本基本上可以找到从一个节点到另一个节点的所有可能路径,但是它不计算任何可能的“周期”(我使用的图形是循环的)。因此,基本上,没有一个节点会在同一路径中出现两次。如果图是非循环的,那么我想您可以说它似乎找到了两个节点之间的所有可能路径。它似乎工作得很好,并且在我的图形大小约为150的情况下,它几乎可以立即在我的计算机上运行,尽管我确信运行时间必须是指数级的,因此随着图变大。
这是一些Java代码,演示了我实现的内容。我确信也必须有更有效或更优雅的方法来做到这一点。
Stack connectionPath = new Stack();
List<Stack> connectionPaths = new ArrayList<>();
// Push to connectionsPath the object that would be passed as the parameter 'node' into the method below
void findAllPaths(Object node, Object targetNode) {
for (Object nextNode : nextNodes(node)) {
if (nextNode.equals(targetNode)) {
Stack temp = new Stack();
for (Object node1 : connectionPath)
temp.add(node1);
connectionPaths.add(temp);
} else if (!connectionPath.contains(nextNode)) {
connectionPath.push(nextNode);
findAllPaths(nextNode, targetNode);
connectionPath.pop();
}
}
}
我要给你一个(有点小)版本(尽管我可以理解)的科学证明,证明您在可行的时间内无法做到这一点。
我要证明的是,枚举任意图中两个选定的不同节点(例如s
和t
)之间的所有简单路径的时间复杂度G
不是多项式。注意,由于我们仅关心这些节点之间的路径数量,因此边缘成本并不重要。
当然,如果图形具有一些精心选择的属性,则这很容易。我正在考虑一般情况。
假设我们有一个多项式算法,列出了s
和之间的所有简单路径t
。
如果G
已连接,则列表为非空。如果G
not和s
andt
位于不同的组件中,则列出它们之间的所有路径确实很容易,因为没有路径!如果它们在同一个组件中,我们可以假装整个图仅由该组件组成。因此,我们假设G
确实已连接。
然后,列出的路径数必须是多项式,否则算法无法将它们全部归还给我。如果它枚举了所有这些,它必须给我最长的一个,所以它就在那里。有了路径列表,可以应用一个简单的过程来指向我这是最长的路径。
我们可以证明(尽管我无法想出一种有凝聚力的说法),这条最长的路径必须遍历的所有顶点G
。因此,我们刚刚找到了具有多项式过程的哈密顿路径!但这是一个众所周知的NP难题。
然后我们可以得出结论,除非P = NP,否则我们认为存在的这种多项式算法极不可能存在。
G
”的主张不一定成立。是对的吗?
n-1
,则存在。如果不是,则不可能有这样的路径,否则它将比您知道的最长的路径更长。
这是一种使用DFS修改查找和打印从s到t的所有路径的算法。动态编程也可以用于查找所有可能路径的计数。伪代码将如下所示:
AllPaths(G(V,E),s,t)
C[1...n] //array of integers for storing path count from 's' to i
TopologicallySort(G(V,E)) //here suppose 's' is at i0 and 't' is at i1 index
for i<-0 to n
if i<i0
C[i]<-0 //there is no path from vertex ordered on the left from 's' after the topological sort
if i==i0
C[i]<-1
for j<-0 to Adj(i)
C[i]<- C[i]+C[j]
return C[i1]
这个问题现在有点老了……但是我会把我的帽子扔进去。
我个人发现一种find_paths[s, t, d, k]
有用形式的算法,其中:
用无穷大的编程语言的形式d
,并k
会给你所有paths§。
§显然,如果您使用的是有向图,并且希望它们之间所有无方向的路径s
,t
则必须同时运行以下两种方法:
find_paths[s, t, d, k] <join> find_paths[t, s, d, k]
我个人喜欢递归,尽管有时可能会很困难,但无论如何首先要定义我们的辅助函数:
def find_paths_recursion(graph, current, goal, current_depth, max_depth, num_paths, current_path, paths_found)
current_path.append(current)
if current_depth > max_depth:
return
if current == goal:
if len(paths_found) <= number_of_paths_to_find:
paths_found.append(copy(current_path))
current_path.pop()
return
else:
for successor in graph[current]:
self.find_paths_recursion(graph, successor, goal, current_depth + 1, max_depth, num_paths, current_path, paths_found)
current_path.pop()
这样一来,核心功能就变得微不足道了:
def find_paths[s, t, d, k]:
paths_found = [] # PASSING THIS BY REFERENCE
find_paths_recursion(s, t, 0, d, k, [], paths_found)
首先,让我们注意几件事:
[]
是未初始化的列表,请将其替换为您选择的编程语言的等效列表paths_found
通过引用传递。显然,递归函数不会返回任何内容。适当处理。graph
假设某种形式的hashed
结构。有多种实现图形的方法。无论哪种方式,graph[vertex]
都可以在有向图中获取相邻顶点的列表-进行相应调整。如果您真正关心的是从最短路径到最长路径的顺序排序,那么使用改进的A *或Dijkstra算法会更好。稍作修改,该算法将以最短路径优先的顺序返回所需的尽可能多的路径。因此,如果您真正想要的是从最短到最长的所有可能路径排序,那么这就是要走的路。
如果您想要一个基于A *的实现,该实现能够返回从最短到最长的所有路径排序,那么下面的操作将实现这一点。它有几个优点。首先,它可以有效地从最短到最长排序。此外,它仅在需要时才计算每个其他路径,因此如果您由于不需要每个路径而提早停止,则可以节省一些处理时间。每次计算下一条路径时,它还会为后续路径重用数据,因此效率更高。最后,如果找到了所需的路径,则可以中止,从而节省一些计算时间。总体而言,如果您关心按路径长度排序,那么这应该是最有效的算法。
import java.util.*;
public class AstarSearch {
private final Map<Integer, Set<Neighbor>> adjacency;
private final int destination;
private final NavigableSet<Step> pending = new TreeSet<>();
public AstarSearch(Map<Integer, Set<Neighbor>> adjacency, int source, int destination) {
this.adjacency = adjacency;
this.destination = destination;
this.pending.add(new Step(source, null, 0));
}
public List<Integer> nextShortestPath() {
Step current = this.pending.pollFirst();
while( current != null) {
if( current.getId() == this.destination )
return current.generatePath();
for (Neighbor neighbor : this.adjacency.get(current.id)) {
if(!current.seen(neighbor.getId())) {
final Step nextStep = new Step(neighbor.getId(), current, current.cost + neighbor.cost + predictCost(neighbor.id, this.destination));
this.pending.add(nextStep);
}
}
current = this.pending.pollFirst();
}
return null;
}
protected int predictCost(int source, int destination) {
return 0; //Behaves identical to Dijkstra's algorithm, override to make it A*
}
private static class Step implements Comparable<Step> {
final int id;
final Step parent;
final int cost;
public Step(int id, Step parent, int cost) {
this.id = id;
this.parent = parent;
this.cost = cost;
}
public int getId() {
return id;
}
public Step getParent() {
return parent;
}
public int getCost() {
return cost;
}
public boolean seen(int node) {
if(this.id == node)
return true;
else if(parent == null)
return false;
else
return this.parent.seen(node);
}
public List<Integer> generatePath() {
final List<Integer> path;
if(this.parent != null)
path = this.parent.generatePath();
else
path = new ArrayList<>();
path.add(this.id);
return path;
}
@Override
public int compareTo(Step step) {
if(step == null)
return 1;
if( this.cost != step.cost)
return Integer.compare(this.cost, step.cost);
if( this.id != step.id )
return Integer.compare(this.id, step.id);
if( this.parent != null )
this.parent.compareTo(step.parent);
if(step.parent == null)
return 0;
return -1;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Step step = (Step) o;
return id == step.id &&
cost == step.cost &&
Objects.equals(parent, step.parent);
}
@Override
public int hashCode() {
return Objects.hash(id, parent, cost);
}
}
/*******************************************************
* Everything below here just sets up your adjacency *
* It will just be helpful for you to be able to test *
* It isnt part of the actual A* search algorithm *
********************************************************/
private static class Neighbor {
final int id;
final int cost;
public Neighbor(int id, int cost) {
this.id = id;
this.cost = cost;
}
public int getId() {
return id;
}
public int getCost() {
return cost;
}
}
public static void main(String[] args) {
final Map<Integer, Set<Neighbor>> adjacency = createAdjacency();
final AstarSearch search = new AstarSearch(adjacency, 1, 4);
System.out.println("printing all paths from shortest to longest...");
List<Integer> path = search.nextShortestPath();
while(path != null) {
System.out.println(path);
path = search.nextShortestPath();
}
}
private static Map<Integer, Set<Neighbor>> createAdjacency() {
final Map<Integer, Set<Neighbor>> adjacency = new HashMap<>();
//This sets up the adjacencies. In this case all adjacencies have a cost of 1, but they dont need to.
addAdjacency(adjacency, 1,2,1,5,1); //{1 | 2,5}
addAdjacency(adjacency, 2,1,1,3,1,4,1,5,1); //{2 | 1,3,4,5}
addAdjacency(adjacency, 3,2,1,5,1); //{3 | 2,5}
addAdjacency(adjacency, 4,2,1); //{4 | 2}
addAdjacency(adjacency, 5,1,1,2,1,3,1); //{5 | 1,2,3}
return Collections.unmodifiableMap(adjacency);
}
private static void addAdjacency(Map<Integer, Set<Neighbor>> adjacency, int source, Integer... dests) {
if( dests.length % 2 != 0)
throw new IllegalArgumentException("dests must have an equal number of arguments, each pair is the id and cost for that traversal");
final Set<Neighbor> destinations = new HashSet<>();
for(int i = 0; i < dests.length; i+=2)
destinations.add(new Neighbor(dests[i], dests[i+1]));
adjacency.put(source, Collections.unmodifiableSet(destinations));
}
}
上面代码的输出如下:
[1, 2, 4]
[1, 5, 2, 4]
[1, 5, 3, 2, 4]
请注意,每次致电 nextShortestPath()
它会根据需要为您生成下一条最短路径。它仅计算所需的额外步骤,并且不会遍历任何旧路径两次。而且,如果您决定不需要所有路径并尽早结束执行,则可以节省大量的计算时间。您只计算最多所需的路径数,而无需再计算。
最后应该指出的是,尽管我认为A *和Dijkstra算法不会对您造成影响,但确实存在一些较小的限制。也就是说,它在权重为负的图形上无法正常工作。
这是JDoodle的链接,您可以在其中自行在浏览器中运行代码并查看其工作情况。您还可以在图表周围进行更改,以显示它也可以在其他图表上使用:http : //jdoodle.com/a/ukx
以下功能(经过修改的BFS,在两个节点之间具有递归路径查找功能)将完成非循环图的工作:
from collections import defaultdict
# modified BFS
def find_all_parents(G, s):
Q = [s]
parents = defaultdict(set)
while len(Q) != 0:
v = Q[0]
Q.pop(0)
for w in G.get(v, []):
parents[w].add(v)
Q.append(w)
return parents
# recursive path-finding function (assumes that there exists a path in G from a to b)
def find_all_paths(parents, a, b):
return [a] if a == b else [y + b for x in list(parents[b]) for y in find_all_paths(parents, a, x)]
例如,下图(DAG)G
由
G = {'A':['B','C'], 'B':['D'], 'C':['D', 'F'], 'D':['E', 'F'], 'E':['F']}
如果要查找和之间的所有路径(使用上面定义的函数作为),它将返回以下路径:'A'
'F'
find_all_paths(find_all_parents(G, 'A'), 'A', 'F')
我认为您想要的是基于BFS的某种形式的Ford-Fulkerson算法。它通过查找两个节点之间的所有扩展路径来计算网络的最大流量。
http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm
有一篇不错的文章可能会回答您的问题/仅打印路径而不是收集路径/。请注意,您可以在在线IDE中试用C ++ / Python示例。
http://www.geeksforgeeks.org/find-paths-given-source-destination/