在OCaml中BFS迷宫求解算法有问题

时间:2013-05-31 16:59:44

标签: ocaml breadth-first-search maze

http://ideone.com/QXyVzR

上面的链接包含我用BFS算法解决迷宫的程序。迷宫表示为2D数组,最初以数字形式传入(0表示可以访问的空块,任何其他数字表示“墙”块),然后转换为我定义的记录类型,保留跟踪各种数据:

type mazeBlock = {
    walkable   : bool;
    isFinish   : bool;
    visited    : bool;
    prevCoordinate : int * int
}

输出是有序对(坐标/索引)的列表,它从开始到结束追踪通过迷宫的最短路径,其坐标都作为参数传递。

对于具有较低分支因子的较小迷宫它可以正常工作,但是当我在较大的迷宫(例如16 x 16或更大)上进行测试时,特别是在没有墙壁(高分支因子)的迷宫上,它会占用大量的时间和记忆。我想知道这是算法固有的还是与我实现它的方式有关。任何OCaml黑客都可以向我提供他们的专业知识吗?

另外,我对OCaml的经验很少,所以对于如何在风格上改进代码的任何建议都将非常感激。谢谢!


编辑:

http://ideone.com/W0leMv

这是该程序的清理,编辑版本。我修正了一些风格问题,但我没有改变语义。像往常一样,第二次测试仍占用大量资源,似乎根本无法完成。仍然在这个问题上寻求帮助......

EDIT2:

解决。非常感谢两位回答者。这是最终的代码:

http://ideone.com/3qAWnx

2 个答案:

答案 0 :(得分:2)

在您的关键部分,即mazeSolverLoop,您应该只访问之前未访问过的元素。从队列中获取元素时,首先应检查元素是否已被访问,在这种情况下,除了递归以获取下一个元素之外什么也不做。这正是使算法具有良好时间复杂性的原因(您从未访问过两次)。

否则,是的,您的OCaml风格可以改进。一些评论:

  • OCaml-land中的约定是write_like_this而不是writeLikeThis。我建议您遵循它,但不可否认,这是一个品味而不是客观标准。

  • 如果数据结构是一个已更新的可变结构,则返回数据结构是没有意义的;为什么你总是返回一个(grid, pair)队列,当它与输入完全相同时?您可以让这些函数返回unit并使代码更简单,更易于阅读。

  • 成对允许的抽象级别是好的,你应该保留它;你目前没有。例如,let (foo, bar) = dimension grid in if in_bounds pos (foo, bar)没有必要写作。只需将尺寸命名为dim而不是(foo, bar),如果您不需要将它们分开,则将其拆分为两个组件是没有意义的。请注意,对于邻居,您现在使用neighborXneighborY进行数组访问,但这是一个样式错误:您应该有辅助函数来获取和设置数组中的值,取一对作为输入,这样您就不必在main函数中破坏该对。尝试将所有代码保存在同一个抽象级别的单个函数中:所有代码都在不同的坐标上工作,或者所有工作在对上(这样做而不是一直构造/解构)。

答案 1 :(得分:0)

如果我理解你,对于没有墙的N x N网格,你有一个N ^ 2个节点和大约4 * N ^ 2个边的图。对于N = 16,这些似乎不是大数字。

我要说的唯一技巧是确保正确跟踪访问过的节点。我浏览了你的代码,并没有看到任何明显错误的方式。

这是一个很好的OCaml成语。你的代码说:

let isFinish1 = mazeGrid.(currentX).(currentY).isFinish in
let prevCoordinate1 = mazeGrid.(currentX).(currentY).prevCoordinate in
mazeGrid.(currentX).(currentY) <-
    { walkable = true;
     isFinish = isFinish1;
     visited = true;
     prevCoordinate = prevCoordinate1}

你可以更经济地说这个:

mazeGrid.(currentX).(currentY) <-
    { mazeGrid.(currentX).(currentY) with visited = true }