找到距离最远的两个点的算法

时间:2009-01-25 11:35:16

标签: algorithm math path distance path-finding

我正在寻找一种用于我正在制作的赛车游戏的算法。地图/关卡/轨道是随机生成的,所以我需要找到两个位置,即开始和目标,它们利用了大部分地图。

  • 算法是在二维空间内工作
  • 从每个点开始,只能遍历四个方向的下一个点;上,下,左,右
  • 点数只能是阻止或非阻止,只能遍历非阻塞点

关于距离的计算,它不应该是缺乏更好词的“鸟道”。如果它们之间有墙(或其他阻挡区域),则A和B之间的路径应该更长。

我不确定从哪里开始,非常欢迎评论,并且在伪代码中首选提出的解决方案。

编辑:是的。在浏览gs's code后,我又给了它一个镜头。我这次用C ++写的,而不是python。但是,即使在阅读了Dijkstras algorithmfloodfillHosam Alys solution之后,我仍然没有发现任何重要的区别。我的代码仍然有效,但没有你想要运行的那么快。完整来源位于pastie。唯一有趣的线(我猜)是第78-118行的Dijkstra变体。

但速度不是这里的主要问题。如果有人能够指出算法中的差异,我真的很感激帮助。

  • 在Hosam Alys算法中,他是从边界而不是每个节点扫描的唯一区别吗?
  • 在Dijkstras你跟踪和覆盖走路的距离,但不是在洪水中,但那就是它?

9 个答案:

答案 0 :(得分:10)

假设地图是矩形的,您可以遍历所有边界点,并开始填充填充以找到起点的最远点:

bestSolution = { start: (0,0), end: (0,0), distance: 0 };
for each point p on the border
    flood-fill all points in the map to find the most distant point
    if newDistance > bestSolution.distance
        bestSolution = { p, distantP, newDistance }
    end if
end loop

我想这会在O(n^2)。如果我没有弄错,那就是(L+W) * 2 * (L*W) * 4,其中L是长度,W是地图的宽度,(L+W) * 2表示周边边界点的数量, (L*W)是点数,而4是洪水填充最多可以访问4点(从所有方向)的假设。由于n等于点数,因此相当于(L + W) * 8 * n,这应该优于O(n 2 )。 (如果地图是方形的,则顺序为O(16n 1.5 )。)

更新:根据评论,因为地图更像迷宫(比我最初想的那样有一个简单障碍的地图),你可以在上面做出相同的逻辑,但检查所有点在地图中(而不是边框​​上的点)。这应该是O(4n 2 )的顺序,这仍然比F-W和Dijkstra都要好。

注意: Flood filling更适合此问题,因为所有顶点仅通过4个边框直接连接。地图的广度优先遍历可以相对快速地产生结果(仅O(n))。我假设每个点可以在其4个邻居中的每一个的洪水填充中进行检查,因此上面公式中的系数。

更新2:我很感谢我收到的有关此算法的所有积极反馈。特别感谢@Georg his review

P.S。欢迎任何评论或更正。

答案 1 :(得分:9)

跟进关于Floyd-Warshall的问题或Hosam Aly的简单算法:

我创建了一个可以使用这两种方法的测试程序。这些是文件:

在所有测试案例中,Floyd-Warshall的速度要慢一些,这可能是因为有助于此算法实现此目的的边缘数量非常有限。

这些时候,每次这个领域都是四胞胎,10个领域中有3个是障碍。

Size         Hosam Aly      Floyd-Warshall
(10x10)      0m0.002s       0m0.007s     
(20x20)      0m0.009s       0m0.307s
(40x40)      0m0.166s       0m22.052s
(80x80)      0m2.753s       -
(160x160)    0m48.028s      -

Hosam Aly的时间似乎是二次方,因此我建议使用该算法。  Floyd-Warshall的内存消耗也是n 2 ,显然超过了需要。 如果你知道为什么Floyd-Warshall如此之慢,请发表评论或编辑这篇文章。

PS:我很久没有写过C或C ++,我希望自己没有犯过太多错误。

答案 2 :(得分:5)

我删除了推荐Floyd-Warshall算法的原始帖子。 :(

gs did a realistic benchmark并猜测,对于典型的地图尺寸,F-W比Hosam Aly的“洪水填充”算法慢得多!因此,即使F-W是一个很酷的算法,并且比Dijkstra的密集图快得多,我不能再推荐它的OP问题了,它涉及非常稀疏的图形(每个顶点只有4条边)。

记录:

  • Dijkstra's algorithm的有效实现需要O(Elog V)时间来获得具有E边和V顶点的图。
  • Hosam Aly的“洪水填充”是breadth first search,即O(V)。这可以被认为是Dijkstra算法的一个特例,其中没有顶点可以修改其距离估计。
  • Floyd-Warshall algorithm需要O(V ^ 3)时间,编码非常容易,并且对于密集图形(顶点通常连接到许多其他顶点的图形)仍然是最快的。但OP的任务不是正确的选择,这涉及非常稀疏的图形。

答案 3 :(得分:5)

听起来你想要的是由graph diameter分隔的终点。一个相当好且易于计算的近似是选择一个随机点,从中找到最远点,然后从那里找到最远点。最后两点应该接近最大分离。

对于长方形迷宫,这意味着两次洪水填充应该会为你提供一对非常好的起点和终点。

答案 4 :(得分:3)

Raimund Seidel在他论文的第一部分On the All-Pairs-Shortest-Path Problem in Unweighted Undirected Graphs [pdf]中给出了一种使用矩阵乘法计算未加权无向图上的全对距离矩阵的简单方法(这正是你想要的)。

输入是邻接矩阵,输出是全对最短路径距离矩阵。对于n个点,运行时间为O(M(n)* log(n)),其中M(n)是矩阵乘法算法的运行时间。

如果您需要,本文还提供了计算实际路径的方法(在相同的运行时间内)。

Seidel的算法很酷,因为运行时间与边数无关,但我们实际上并不在意,因为我们的图是稀疏的。但是,如果你想要所有对距离矩阵,这可能仍然是一个不错的选择(尽管比n ^ 2运行时稍差),这也可能比迷宫中的floodfill更容易实现和调试。 / p>

这是伪代码:

Let A be the nxn (0-1) adjacency matrix of an unweighted, undirected graph, G

All-Pairs-Distances(A)
    Z = A * A
    Let B be the nxn matrix s.t. b_ij = 1 iff i != j and (a_ij = 1 or z_ij > 0)
    if b_ij = 1 for all i != j return 2B - A //base case
    T = All-Pairs-Distances(B)
    X = T * A
    Let D be the nxn matrix s.t. d_ij = 2t_ij if x_ij >= t_ij * degree(j), otherwise d_ij = 2t_ij - 1
    return D

要获得距离最远的一对点,我们只返回argmax_ij(d_ij)

答案 5 :(得分:1)

完成了针对问题的dijkstra解决方案的python模型。 代码有点长,所以我把它贴在其他地方:http://refactormycode.com/codes/717-dijkstra-to-find-two-points-furthest-away-from-each-other

在我设置的大小中,为一个节点运行算法大约需要1.5秒。为每个节点运行它需要几分钟。

虽然似乎没有工作,但它总是以最长的路径显示出来的并且是直角。 58个瓷砖。当你没有障碍时,这当然是真的。但即使添加几个随机放置的程序,该程序仍然发现一个最长的。也许它仍然是真的,没有更高级的形状就难以测试。

但也许它至少可以表明我的抱负。

答案 6 :(得分:1)

好的,“Hosam的算法”是广泛的第一次搜索,在节点上有预选。 Dijkstra的算法不应该在这里应用,因为你的边没有权重。

区别是至关重要的,因为如果边的权重变化,则需要保持许多选项(备用路线)打开并检查每一步。这使得算法更复杂。 通过广度优先搜索,您只需以一种方式探索所有边缘,即获得找到每个节点的最短路径。即按照你找到它们的顺序探索边缘。

所以基本上差异是Dijkstra必须“回溯”并查看它之前探索过的边缘以确保它遵循最短路线,而广度优先搜索总是知道它遵循最短路线。

此外,在迷宫中,外边界上的点不能保证是最长路线的一部分。 例如,如果你有一个巨大螺旋状的迷宫,但外端回到中间,你可以在螺旋的中心有两个点,螺旋的末端有两个点,两个在中间!

所以,这样做的一个好方法是从每个点使用广度优先搜索,但在搜索后删除起点(您已经知道往返于它的所有路径)。 首先是广度的复杂度是O(n),其中n = | V | + | E |。我们对V中的每个节点执行一次,因此它变为O(n ^ 2)。

答案 7 :(得分:0)

您的说明听起来像maze routing问题。查看Lee Algorithm。有关VLSI设计中布局布线问题的书籍可能对您有所帮助 - Sherwani's "Algorithms for VLSI Physical Design Automation"很好,您可能会发现VLSI Physical Design Automation by Sait and Youssef有用(并且在其Google版本中更便宜......)

答案 8 :(得分:0)

如果您的对象(点)不经常移动,您可以在比O(n ^ 3)时间短的时间内执行此类计算。

您只需要将空间划分为大网格并预先计算网格间距离。然后选择占据最远距离网格的点对是简单的表查找问题。在一般情况下,您需要成对检查一小组对象。

如果距离指标是连续的,则此解决方案有效。因此,例如,如果地图中存在许多障碍(如在迷宫中),则此方法可能会失败。