- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在研究一种算法,用于在网格中找到一组不相交的路径给定点对..对于这些对,就像这样:(9,4) 和 (12,13)
输出应该是这样的:
9,10,11,7,3,4
13,14,15,16,12
如果不能路由所有路径则打印“Blocked”
首先我搜索了一个已经制定的算法来找到 2 之间的所有简单路径图形或网格中的点。我通过@Casey Watson 和@svick here 找到了这个..它工作得很好,但只适用于小图。
我将它转换为 C#.NET 并对其进行了一些增强,以便能够找到最大长度 X. 并以此为基础构建我的总体算法。
我构建的那个在小图表中运行良好..这是 8x8 网格中的 9 对路线。
但在较大的模型(如 16x16)甚至我打算做的最后一个模型(16x16x2 的 3D 模型)中需要花费大量时间像这样
该算法被开发为深度优先搜索RECURSIVE算法,但它花了很长时间才将值(value)返回给用户。所以我决定将其转换为循环而不是递归调用,这样我就可以受益于 .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。将它们视为复合数字字符串 (不是十进制值)充当基于维度的节点地址。
在 3 维网格中,它们的编号为 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 的一系列有效路径,方法是将一个维度差加到你计划移动的任何方向(在 2-D 数组中你只能选择两种方式之一。在 3-D 中你有三个选择)。
每个答案都应该是尽可能短的距离。这种方法的计算时间应该是毫秒。在 Steam 驱动的 ZX81 上!
关于c# - 在大网格中路由路径的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13054708/
您能否建议如何在 Bootstrap 或 IE 兼容的 CSS 网格中,在没有 CSS 网格的情况下进行以下布局。 在大屏幕中 头部,左侧堆叠的 body 和右侧覆盖头部和 body 高度的图像。 [
我想在 Objective-C 中绘制一个 15*15 的网格。格子颜色是蓝色的,就像在诺基亚制作“贪吃蛇”游戏的棋盘一样。 我试过使用 for 循环来创建 subview ,但它似乎不起作用,我查看
我正在尝试将 CSS 网格与 grid-template-columns: repeat(auto-fill, auto) 一起使用,单元格被设置为最大宽度,导致每行一个元素。 p> 是否可以让元素宽
我正在努力在网格的自定义列上添加一个指向网站的简单、简单的链接。我用了 Inchoo blog为列添加自定义渲染器,它可以工作。我认为只需修改渲染并添加标签就足够了。但我的希望破灭了,行不通。 如何做
使用 Gnuplot 我绘制了下图 - 现在,正如您在图像中看到的那样,很难在线条之间识别出其末端的块。所以我想用不同的颜色或样式交替着色网格。 我现在用来给网格着色的代码是 - set style
假设我有一个非常简单的 WPF 网格(6 行 x 6 列),定义如下:
我有一个希望绑定(bind)到 WPF 网格的集合。 我面临的问题是列数是动态的并且取决于集合。这是一个简单的模型: public interface IRows { string Messa
我正在使用 Vaadin 8,我想制作某种混淆矩阵。我想知道是否可以根据单元格位置而不是数据提供者手动填充表格/网格的值。 referenceTable.addColumn(reference ->
我在 http://jsfiddle.net/TsRJy/ 上创建了一个带有 div 框的网格. 问题 我不知道如何使 a:hover 工作。 信息 重写 HTML 代码,因为表格不适合我。 http
银光处女在这里。如何使网格周围的用户控件自动调整大小以适应内部网格宽度?目前,当浏览器窗口更宽时,用户控件的显示尺寸约为 300 或 400 像素。它在数据网格周围呈现垂直和水平滚动条,这很丑陋。我想
这个问题已经有答案了: Equal width columns in CSS Grid (11 个回答) 已关闭 2 年前。 使用 CSS Grid,当您不知道会有多少个子项时,如何将所有子项保留在一
我想使用 CSS Grid 的 grid-template-areas。 但问题是我正在使用的 CMS 添加了大量额外的包装器。有没有办法忽略额外的包装?因为它弄乱了漂亮的网格区域...... 我正在
在我的Grid中,当我单击“操作”按钮(下面的代码中显示的“删除和编辑”按钮)时,我需要弹出一个窗口,而不用警告消息提醒用户; 在下面的代码中,我正在使用HANDLER handler: button
这个问题已经有答案了: Equal width columns in CSS Grid (11 个回答) 已关闭 2 年前。 使用 CSS Grid,当您不知道会有多少个子项时,如何将所有子项保留在一
我需要模拟一个仓库,其中有几辆自动驾驶车辆在给定的布局上移动,并具有简单的优先级规则。根据我的理解,这个问题可以通过离散事件模拟(DES)轻松解决,我会使用 SimPy为了这。 我看到的问题是,我似乎
在 ASP.NET 中,我可以让用户控件在页面上的表格中占据多个单元格: 用户控件1: foo bar 第1页: 并且自动调整列宽以适应最大的用户控件。 这也可以在 WPF
我正在寻找一种方法来实时搜索我的网格+要过滤的复选框。我有一个包含学生的网格(照片和姓名)。我想要的是有一个复选框,可以过滤学生所在的不同类(class)。还有一个搜索栏,我可以在其中输入学生姓名。
我正在使用 jQuery 和 jQuery UI 构建一个 Web 应用程序。我陷入了僵局。我需要的是一个 jQuery 网格,它具有可编辑字段,并以某种方式在这些可编辑单元格之一上合并一个自动完成字
我想知道是否有其他 JavaScript 组件可以提供具有多个分组的网格表示。下面是jqGrid的截图我扩展了允许该功能,但它需要获取所有数据。我希望在扩展分组时加载数据。 另一个修改后的 jqGri
我一直在为我将在此处描述的 CSS 问题而烦恼: 在下面的示例 ( https://codesandbox.io/s/jjq4km89y5 ) 中,您可以看到一个可滚动的内容(紫色背景)和一个被左侧面
我是一名优秀的程序员,十分优秀!