我正在开发一种将对象与电线连接起来的软件。该布线具有这样的规则:这些线不能通过其他物体并且不接受对角线移动。
我所知道的所有最短路径算法(A *,dijkstra等)都找到了这种类型的路径:
我不希望在第二个屏幕截图中出现不必要的曲折。我如何实现这一目标?
注意:任何想要尝试这些算法的人都可以使用this应用程序。
另一个注意:这是我不想要的确切情况。它找到了之字形路径,而不是“向右移动,直到你到达目标的x位置,向上移动直到你到达目标的y位置”,这与Z字形的成本相同。
答案 0 :(得分:17)
您当前的算法找到最短路径Pmin
,但改进的算法应找到最小路径(Pmin, Tmin)
。一般解决方案要求您使用一对数字而不是一个数字。如果新发现的Pnew
小于当前Pmin
,或者如果它相等但Tnew
小于Tmin
,则将(Pnew, Tnew)
作为新的最小路径。
如果电路板足够小,您仍然可以使用当前使用的单个数字,但此数字必须是复合数字C = P * N + T
,其中N
足够大且常数足够小。它必须大于该板的最大可能T,这几乎是板中的瓦片总数。它必须足够小,以便在算法碰巧处理板中的最大路径时没有整数溢出,这也是板中的瓦片总数。所以N
必须满足这两个术语(B是棋盘中的瓦片总数):
N > B
B * N < INT_MAX
如果B
大于SQRT(INT_MAX)
,则此系统无法解析,您应该使用一对值。 N
应为SQRT(INT_MAX)
,其中2 32 为2 16 。
现在的主要问题是如何计算所有转弯,但这取决于您拥有的算法。添加该部分应该不会太难。
答案 1 :(得分:8)
直观地说,你可以通过给你的代理人一个“动力”来做到这一点。
具体来说,将状态空间的大小增加四倍;您可以跟踪代理上次,向右,向左还是向下移动的情况。通过一些大的因素来扩大网络中的成本,并为改变方向的移动分配一个小的惩罚。
答案 2 :(得分:4)
使用一对值(双精度,整数等)进行距离计算。
第一个是距离,第二个是转弯数。
按词汇排序,因此第一个比第二个重要。
这比数学上和编程上的“轮流使用小额罚款”更清晰。
每个节点都是重复的。节点“已经垂直进入”和“已经水平进入”,因为它们对转弯数有所不同。
启发式是曼哈顿距离,如果你不是与目标完全水平或垂直对齐,则转弯。
作为一个缺点,这种技术妨碍了跳跃点优化,因为到一个位置的对称路径要少得多(因为有些路径比其他路径有更多的转弯)。
答案 3 :(得分:3)
在内部,算法会评估许多可能的路径,并选择最短的路径。
如果你稍微调整算法,那么它计算每次方向变化的额外惩罚,那么它将选择方向变化最小的最短路径。
答案 4 :(得分:1)
我并不熟悉搜索算法,但这将是最好的编程方法,在下面伪造。
我们使用的对象:
vertex { //generic x,y coordinate
int x;
int y;
}
vertices[3]; //3 vertices: 0, 1, 2 (0 is start, 1 is mid, 2 is end);
我们的算法,它取决于已发现的最有效的路径没有像¯| _ |¯
这样的奇怪boolean hasObstacles = false;
int increment = 0;
//there's some other ways to set this up, but this should make the most sense to explaining which way we need to go
if(vertices[0].x < vertices[2].x)
increment = 1;
else
increment = -1;
for(int i = vertices[0].x; i != vertices[2].x; i = i + increment) {
//check for collision at coordinate (i, vertices[0].y), set hasObstacles to true if collision
}
if(vertices[0].y < vertices[2].y)
increment = 1;
else
increment = -1;
for(int i = vertices[0].y; i != vertices[2].y; i = i + increment) {
//check for collision at coordinate (vertices[2].x, i), set hasObstacles to true if collision
}
if(!hasObstacles) {
//we can remove these 3 vertices and add this point to our list of vertices.
vertex corner = new vertex(vertices[2].x, vertices[0].y) // relocation of point
}
扫描应该一次进行一个顶点。如果3个顶点被单个顶点替换,则下一次扫描应该将该新顶点用作0。
答案 5 :(得分:1)
现在你的算法回答问题&#34;最佳路径经过哪个方块?&#34;您的图形具有每个正方形的节点和每对相邻正方形的边缘。
将其更改为&#34;最佳路径在哪里穿过正方形之间的边界?&#34;
您的图表会发生变化:
现在您可以对相对的方形边缘和相邻方形边缘的连接进行不同的连接定价。给予第二个更大的权重将减少曲折的数量。
答案 6 :(得分:1)
你的问题非常重要,例如如果你贪婪地走向尽可能向上或向右,那么你可能会遇到一个紧张的迷宫般的物体需要一条疯狂的锯齿形路径才能完成,而如果你在紧张的迷宫前停下来,你可能会改变通过基本绕过迷宫,方向更少。你可以在你的道路上的任何地方遇到这种困境,而不仅仅是在开始时。解决此问题的一种方法是使用Dijkstra并定义可以前往的位置网格,然后将移动定义为2步长而不是1步长。如果移动在一个定向方向上是纯水平或纯垂直,则定义两个连接的网格点之间的距离非常小,如果移动在中间改变方向,则定义非常大。然后,假设从开始到结束的路径长度是偶数,在这个双移框架中从开始到结束的最短路径将是最小化之字形量的路径。如果从开始到结束的路径长度是奇数,则使用水平和垂直一个空格的网格点开始,然后从开始到结束的路径长度将是均匀的(尽管您必须为两者运行路径查找可能修改的起始位置)。
答案 7 :(得分:1)
您只需稍微修改A *算法的启发式算法即可。一个是我的头脑:如果你不喜欢这些转弯,你可以在每个转弯处罚。
所以你的启发式将取决于转弯次数和曼哈顿距目标的距离。重要的是不要忘记它不应该高估到目标的距离。详细了解如何选择heuristic here。