[英]What is the best way to route paths in a large grid?
我正在研究一種算法,在給定的一對點的網格中找到一組非相交路徑。對於這些對,這樣:(9,4)和(12,13)
輸出應該是這樣的:
9,10,11,7,3,4
13,14,15,16,12
如果無法路由所有路徑,則打印“已阻止”
首先,我搜索了一個已經制作的算法,以找到圖形或網格中2個點之間的所有簡單路徑。 我在@Casey Watson和@svick 這里找到了這個..它的效果非常好,但僅適用於小圖。
我將它轉換為C#.NET並對其進行了一些增強,以便能夠找到最大長度為X的路徑。並在其上構建我的總算法。
我建的那個在小圖中工作得很好..這是8x8網格中的9對路線..
但它需要花費大量時間在較大的像16x16甚至是我打算做的最后一個,這是一個16x16x2的3D模型像這樣
該算法被開發為深度優先搜索RECURSIVE算法,但是花費了大量時間將值返回給用戶。 所以我決定將它轉換為循環而不是遞歸調用,以便我可以從.NET中的yield return功能中受益,但它仍然沒有任何幫助。
算法的循環版本在不到一秒的時間內找到一對點的路徑,但遞歸的路徑花費超過90秒。
當我嘗試使用2對時,循環版本需要大約342秒,但遞歸版需要大約200秒。
所以我不知道哪個更快..!? 遞歸或循環一個..
我真的想知道這樣做的最好方法..
注意:節點編號中的第一個數字確定圖層(從1開始)。
這是代碼
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace AlgorithmTest
{
struct Connection
{
public int FirstNode;
public int SecondNode;
public Connection(int N1,int N2)
{
FirstNode = N1;
SecondNode = N2;
}
}
enum Algorithm
{ Recursion, Loops }
public class Search
{
private const int MAX = 15;
private const int Width = 16;
private const int Length = 16;
private const int Height = 2;
private static void Main(string[] args)
{
var graph = new Graph();
var str = new int[Height,Length, Width];
var level = ((int)Math.Pow(10, (Length * Width).ToString().Length) >= 100) ? (int)Math.Pow(10, (Length * Width).ToString().Length) : 100;
for (var i = 0; i < Height; i++)
{
int num = 0;
for (var j = 0; j < Length; j++)
for (var k = 0; k < Width; k++)
{
str[i, j, k] = ++num + level;
}
level += level;
}
for (var i = 0; i < Height; i++)
{
for (var j = 0; j < Length; j++)
{
for (var k = 0; k < Width; k++)
{
if (i < Height - 1) graph.addEdge(str[i, j, k], str[i + 1, j, k]);
if (i > 0) graph.addEdge(str[i, j, k], str[i - 1, j, k]);
if (k < Width - 1) graph.addEdge(str[i, j, k], str[i, j, k + 1]);
if (k > 0) graph.addEdge(str[i, j, k], str[i, j, k - 1]);
if (j < Length - 1) graph.addEdge(str[i, j, k], str[i, j + 1, k]);
if (j > 0) graph.addEdge(str[i, j, k], str[i, j - 1, k]);
}
}
}
var wt = new Stopwatch();
wt.Start();
var connectedNodes = new List<Connection>()
{
new Connection(1030, 1005),
// new Connection(1002, 1044),
// new Connection(1015, 1064),
// new Connection(1041, 1038),
// new Connection(1009, 1027),
// new Connection(1025, 1018),
// new Connection(1037, 1054),
// new Connection(1049, 1060),
// new Connection(1008, 1031),
// new Connection(1001, 1035),
};
wt.Start();
Console.WriteLine("Using Loops:");
Console.WriteLine();
var allPaths = new Search().FindAllPaths(connectedNodes, graph, MAX, Algorithm.Loops);
wt.Stop();
foreach (var path in allPaths)
{
PrintPath(path);
}
Console.WriteLine("Total Seconds: " + wt.Elapsed.TotalSeconds + ", Number of paths: " + allPaths.Count());
Console.WriteLine("***************************************************************************************************");
Console.WriteLine("Using Recursion:");
Console.WriteLine();
wt.Reset();
wt.Start();
allPaths = new Search().FindAllPaths(connectedNodes, graph, MAX, Algorithm.Recursion);
wt.Stop();
foreach (var path in allPaths)
{
PrintPath(path);
}
Console.WriteLine("Total Seconds: " + wt.Elapsed.TotalSeconds + ", Number of paths: " + allPaths.Count());
Console.WriteLine();
}
private IEnumerable<List<int>> FindAllPaths(List<Connection> connectedNodes, Graph graph, int max, Algorithm algorithm)
{
var paths=new Stack<List<int>>();
var blocked=new List<int>();
for (var i = 0; i < connectedNodes.Count; i++)
{
if (!blocked.Contains(connectedNodes[i].FirstNode)) blocked.Add(connectedNodes[i].FirstNode);
if (!blocked.Contains(connectedNodes[i].SecondNode)) blocked.Add(connectedNodes[i].SecondNode);
}
if (algorithm == Algorithm.Recursion)
{
if (FindAllPaths(connectedNodes, 0, max, graph, paths, blocked))
{
Console.WriteLine("BLOCKED");
return new List<List<int>>();
}
}
else if(algorithm==Algorithm.Loops)
{
if (!FindAllPaths2(connectedNodes, 0, max, graph, paths, blocked))
{
Console.WriteLine("BLOCKED");
return new List<List<int>>();
}
}
return paths;
}
private static bool FindAllPaths(List<Connection> connectedNodes,int order,int max, Graph graph, Stack<List<int>> allPaths, List<int> blocked)
{
if (order >= connectedNodes.Count) return false;
var paths = SearchForPaths(graph, connectedNodes[order].FirstNode, connectedNodes[order].SecondNode, max, blocked);
if (paths.Count == 0) return true;
int i;
for (i = 0; i < paths.Count; i++)
{
var path = paths[i];
allPaths.Push(path);
blocked.AddRange(path);
if (!FindAllPaths(connectedNodes, order + 1,max, graph, allPaths, blocked)) break;
allPaths.Pop();
foreach (var j in path)
{
blocked.RemoveAll(num => num==j);
}
paths.RemoveAll(list => IsListsSimilar(list,path));
i--;
}
if (i == paths.Count) return true;
return false;
}
private static bool IsListsSimilar(List<int> L1,List<int> L2)
{
if (L2.Count > L1.Count) return false;
for (int i = 0; i < L2.Count - 1; i++)
{
if (L1[i] != L2[i]) return false;
}
return true;
}
private static List<List<int>> SearchForPaths(Graph graph, int start, int end, int max, List<int> blocked)
{
blocked.Remove(start);
blocked.Remove(end);
var nodePaths = new List<List<int>>();
var visited = new LinkedList<int>();
visited.AddLast(start);
DepthFirstSearch(graph, visited, end, max, blocked, nodePaths);
nodePaths = nodePaths.OrderBy(list => list.Count).ToList();
return nodePaths;
}
private static void DepthFirstSearch(Graph graph, LinkedList<int> visited, int end, int max, List<int> blocked, List<List<int>> paths)
{
var nodes = graph.adjacentNodes(visited.Last.Value);
// examine adjacent nodes
var nodeCount = blocked.Count;
for (int i = 0; i < nodeCount; i++)
{
if (visited.Contains(blocked[i])) return;
}
if (visited.Count > max) return;
nodeCount = nodes.Count;
for (var i = 0; i < nodeCount; i++)
{
if (visited.Contains(nodes[i]) || nodes[i] != end) continue;
visited.AddLast(nodes[i]);
{
paths.Add(new List<int>(visited));
}
visited.RemoveLast();
break;
}
nodeCount = nodes.Count;
for (var i = 0; i < nodeCount; i++)
{
if (visited.Contains(nodes[i]) || nodes[i] == end) continue;
visited.AddLast(nodes[i]);
DepthFirstSearch(graph, visited, end, max, blocked, paths);
visited.RemoveLast();
}
}
private static bool FindAllPaths2(List<Connection> connectedNodes, int order, int max, Graph graph, Stack<List<int>> allPaths, List<int> blocked)
{
if (order >= connectedNodes.Count) return false;
foreach (var path in SearchForPaths2(graph, connectedNodes[order].FirstNode, connectedNodes[order].SecondNode, max, blocked))
{
allPaths.Push(path);
blocked.AddRange(path);
if (!FindAllPaths2(connectedNodes, order + 1, max, graph, allPaths, blocked)) break;
allPaths.Pop();
foreach (var j in path)
{
blocked.RemoveAll(num => num == j);
}
}
return true;
}
private static IEnumerable<List<int>> SearchForPaths2(Graph graph, int start, int end, int max, List<int> blocked)
{
blocked.Remove(start);
blocked.Remove(end);
var visited = new LinkedList<int>();
visited.AddLast(start);
foreach (var VARIABLE in DepthFirstSearch(graph, visited, end, max, blocked))
{
yield return VARIABLE;
}
}
private static IEnumerable<List<int>> DepthFirstSearch(Graph graph, LinkedList<int> visited, int end, int max, List<int> blocked)
{
var nodes = graph.adjacentNodes(visited.Last.Value);
var nodeCount = blocked.Count;
for (int i = 0; i < nodeCount; i++)
{
if (visited.Contains(blocked[i])) yield break;
}
if (visited.Count > max) yield break;
nodeCount = nodes.Count;
for (var i = 0; i < nodeCount; i++)
{
if (visited.Contains(nodes[i]) || nodes[i] != end) continue;
visited.AddLast(nodes[i]);
yield return (new List<int>(visited));
visited.RemoveLast();
break;
}
nodeCount = nodes.Count;
for (var i = 0; i < nodeCount; i++)
{
if (visited.Contains(nodes[i]) || nodes[i] == end) continue;
visited.AddLast(nodes[i]);
foreach (var P in DepthFirstSearch(graph, visited, end, max, blocked))
{
yield return P;
}
visited.RemoveLast();
}
}
private static void PrintPath(List<int> visited)
{
for (int i = 0; i < visited.Count()-1; i++)
{
Console.Write(visited[i]);
Console.Write(" --> ");
}
Console.Write(visited[visited.Count() - 1]);
Console.WriteLine();
Console.WriteLine();
}
}
public class Graph
{
private readonly Dictionary<int, HashSet<int>> map = new Dictionary<int, HashSet<int>>();
public void addEdge(int node1, int node2)
{
HashSet<int> adjacent = null;
map.TryGetValue(node1, out adjacent);
if (adjacent == null)
{
adjacent = new HashSet<int>();
map.Add(node1, adjacent);
}
adjacent.Add(node2);
}
public List<int> adjacentNodes(int last)
{
HashSet<int> adjacent = null;
map.TryGetValue(last, out adjacent);
if (adjacent == null)
{
return new List<int>();
}
return new List<int>(adjacent);
}
}
}
我認為答案在於如何對網格中的節點進行編號。 對於一個簡單的二維網格,4個節點乘4,你可以將它們編號:00,01,02,03,10,11,12 ... 30,31,32,33。把它們想象成復合數字串(充當基於維度的節點地址。
在三維網格中,它們將被編號為000,001,002等,直至330,331,332,333。
如果要查找兩個點10和22之間的所有路徑,可以通過添加尺寸差異來快速計算它們的距離:1y距離2y一個,x0距離x2兩個。 因此節點距離為3,您需要經過3條邊(總共連接4個節點)才能到達目的地。
解決方案空間(可能涉及解決方案路徑的唯一節點)可以通過創建一組嵌入式FOR / NEXT循環來枚舉,每個循環一個。 在這種情況下,10和22的開始和結束值將產生:10,11,12,20,21和22。
現在來了一點聰明。 您可以預先計算(准備)陣列中節點之間的“轉發”連接表。 節點10連接到20和11(均為1維差異)。 從那里你可以生成一系列有效路徑,從10到22,在一個尺寸差異中添加一個你計划移動的方向(在二維數組中,你只能選擇兩種方式中的一種。在3-D中你有三個選擇)。
每個答案應該是盡可能短的距離。 此方法的計算時間應為毫秒。 在蒸汽動力的ZX81上! ; O)
我想提供圖表,但似乎沒有將它們上傳到stackoverflow的工具。
我希望這對你有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.