摆脱迷宫中的老鼠

6

一个老鼠被放置在迷宫中的某个未知位置。

我们只能向上、下、左或右方向移动。我们有两种方法:

  • tryMove(<direction>),如果遇到墙返回false,否则返回true。
  • bool hasLadder():如果有梯子可以逃脱,则返回true。

我们必须编写一个函数explore,如果找到出路则返回true,否则返回false。

这是一个简单的图形问题,如果我们能标记这些位置,就可以使用bfs或dfs算法来解决。如果我们无法标记这些位置,我们可以在循环中移动,重复访问相同的位置。如果迷宫没有标记,有人能帮我把老鼠从迷宫中带出来吗?这可能吗?


7
要求我们为你做功课? - Adam Crossland
3
开始时,老套路就是让你的左手紧贴着墙走。 - deinst
1
@mousey:你的工作是编写软件来让老鼠走出迷宫吗?这是一个经典的作业问题,我很难理解为什么不是作业。 - Adam Crossland
2
@Adam 我正在为面试做准备,这是微软面试中经常被问到的问题。我无法提供更多证据。 - mousey
4
有时候人们会拿一些广泛使用的例子来学习语言。既然你不能确定这是否是情况,就不要假设这是作业。 - James
显示剩余10条评论
8个回答

11

广度优先搜索和深度优先搜索都需要内存,而且一个简单的算法可能会无限循环。老鼠可能只有O(1)的内存。

一种不需要记忆已经到达过的位置的解决方法是随机选择一个方向。虽然解决时间会非常长,但最终应该能够到达每个可到达的方格。这与2D随机行走有关。

令人惊讶的是,在二维晶格上,当步数趋近于无穷大时,随机行走到达任何点(包括起点)的概率是1。


4
如果时间大于无穷大(infinity),则返回假(false)。 - James
3
这个答案肯定能让他在微软得到工作。 - Adam Crossland
1
@Mark Byers,由于您不知道迷宫的大小,甚至无法进行估计... - James
2
@Steven Sudit,你上一次尝试的时间是什么时候呢?我的还在计数中。 - James
3
为什么你会怀疑呢?对我来说,这个假设似乎是完全合理的。认为老鼠的记忆能力取决于其所处迷宫的大小,这个想法似乎非常不可信。 - Mark Byers
显示剩余10条评论

2
该算法基本上是USTCON(无向st连通性):给定一个无向图G,确定从节点s(老鼠的起始位置)到节点t(出口)是否存在路径。这个问题属于复杂度类L,这意味着它需要对数级别的内存。因此,除非您对迷宫的大小有一个上限,或者愿意进行逼近,否则使用固定空间是不可能的。

1
根据Omer Reingold的论文,该算法具有O(log n)的时间复杂度:http://portal.acm.org/citation.cfm?id=1060647 - Mark Byers

1
没有记忆的话,唯一的解决方案就是随机选择,这种方法很糟糕。如果你知道迷宫只有单一连接,你可以采用沿墙走的方法,但如果迷宫中包含一个循环,这种方法可能会陷入无限循环。

0
你应该运行BFS算法。我看到的唯一问题是如何定义图形。它可以使用冯·诺伊曼邻域来定义。节点定义为X,Y对。伪代码应该长这样:
BFS
while (!Q.empty()){
    v = Q.top()
    neighbours = get_adj_list(v)
    foreach (w in neighbours){
        if (isWall(w) || isOutsideMaze(w)) skip
        if (isTerminal(w)) printPathAndExit
        if (dist[v] + 1 < dist[w])
            dist[w] = dist[v] + 1
            Q.push(w)
    }
}

GEN_NEIGHBOURS
dx = {-1,1}
dy = {-1,1}

get_adj_list(v)
    adj_list = []
    for (i in dx)
        for (j in dy)
            adj_list << node(v.x-i,v.y-j)
    return adj_list

编辑:现在我读到内存限制是O(1)。所以我猜你应该遵循旧方法,总是向右转,最终你会走出迷宫或回到起点。

编辑2:来自玉米迷宫提示:

与任何迷宫一样,如果始终向左或向右转,最终你会找到出路。沿着迷宫墙壁走而不抬起手指将使你保持正确的方向。


LOL 试图让老鼠感到疲惫? - Eiko
OP说你没有标记节点为已访问的能力,这意味着你也没有“记住”到达节点的距离的能力。 - Dean J
5
左手靠墙法则只适用于简单连通的迷宫,而这个迷宫可能不是简单连通的。想象一下有围墙环绕的迷宫,围绕一块墙的路径畅通无阻,并有一架梯子在这个区域里。外面的老鼠用左爪顺着墙边走,找不到梯子。 - David Thornley
@David Thornley,你说得对,它只适用于一些特定类型的迷宫,可以称之为经典迷宫。 - jethro
“靠墙算法”变得完全通用,如果你添加规则“每当回到已经去过的区域时,请切换到另一面墙”。基本上,每当你绕圈子时,你就已经探索完了你所在路径的一侧,现在尝试另一侧。当然,这意味着要记住你之前去过哪些地方,或者给你的路径涂上颜色。 - dmckee --- ex-moderator kitten

0
如果我没记错的话,很久以前我有过一个递归作业任务,但它不仅限于O(1)内存。我们只能通过递归来记录走过的路径,无法建立“地图”... 我想这会使用O(n) 的内存,其中n是从起点到梯子的最短距离。
while( 1 )
    {
    if( search( MOVE_LEFT, i ) ||
        search( MOVE_UP, i ) ||
        search( MOVE_RIGHT, i ) ||
        search( MOVE_DOWN, i ) )
         {
         return TRUE;
         }
    i++;
    }

/* recursive function */
bool search( move_type move, long int length )
{

doMove( move ); /* actually move the rat to requested place */

if( hasLadder() )
    return TRUE;

if( 0 == length )
    return FALSE;

switch( move ) /* check each and return rat to previous place */
    {
    case MOVE_LEFT:  
        if( tryMove( MOVE_LEFT ) && search( MOVE_LEFT, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_UP ) && search( MOVE_UP, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_DOWN ) && search( MOVE_RIGHT, length - 1 ) ) return TRUE;      
        doMove( MOVE_RIGHT ); break;
    case MOVE_UP:
        if( tryMove( MOVE_LEFT ) && search( MOVE_LEFT, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_UP ) && search( MOVE_UP, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_RIGHT ) && search( MOVE_RIGHT, length - 1 ) ) return TRUE; 
        doMove( MOVE_DOWN ); break;
    case MOVE_RIGHT:
        if( tryMove( MOVE_UP ) && search( MOVE_UP, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_RIGHT ) && search( MOVE_RIGHT, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_DOWN ) && search( MOVE_RIGHT, length - 1 ) ) return TRUE;      
        doMove( MOVE_LEFT ); break;
    case MOVE_DOWN:
        if( tryMove( MOVE_LEFT ) && search( MOVE_LEFT, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_RIGHT ) && search( MOVE_RIGHT, length - 1 ) ) return TRUE;
        if( tryMove( MOVE_DOWN ) && search( MOVE_RIGHT, length - 1 ) ) return TRUE;      
        doMove( MOVE_UP ); break;
    }

return FALSE;

}   /* search() */

0

这是一个经典问题,通常用作家庭作业。

试试这个:

  1. 你能告诉自己是否正在离开的路上吗?
  2. 一旦你知道了,你需要做什么才能找出自己是否在靠近出口的一步之内?
  3. 如果你距离出口还有两步,该怎么办?

...

正如马克注释的那样,我试图引导你了解的算法的最简版本可能会陷入循环。你该如何解决这个问题?


这看起来非常像Korf的迭代加深搜索。顺便说一下,循环本身只会导致效率低下。只有当你被锁定在一个循环中时才需要担心。 - David Thornley
@David:我之前不知道这个有名字。Korf先生的论文(PDF链接)非常有趣。我特别喜欢在VAX 11/780上使用可行的魔方解法作为例子的做法。 - dmckee --- ex-moderator kitten

0

有趣的是@mousey会问一个关于老鼠的问题...无论如何。

我已经看到这个问题多次出现了。

第一种(天真的)解决方案是沿着左(或右)墙走,然而可能制作特定的迷宫,使老鼠最终跑在圈子里。

事实上,对于我尝试过的每一个确定性移动顺序(如2L、1R、2L、1R等),我们都可以想出一个会让老鼠在圈子里跑的迷宫。当然,循环越复杂,迷宫就越复杂。

因此,如果你有O(1)的内存,唯一的走出迷宫的方法似乎是随机行走,正如Mark Byers所提出的。不幸的是,你不能证明这样的算法无法走出迷宫。

另一方面,如果您具有O(N)的内存,那就归结为创建一个映射(不知何故)。我当时想到的策略是在我经过的每个位置上留下信息素(递增计数器),并且在移动时,在最少被访问的位置中进行选择。但是总是可以找到出路,所以我从来没有考虑过终止条件的情况。
您还可以使用泛洪算法...当然,在我了解BFS术语之前。这样做的好处是您确实有一个终止条件(当没有任何可探索的位置时)。

-1
确定是否有出路对我来说听起来非常像一个停机问题。它可以解决所有简单和许多非简单的情况,但对于许多地图而言,除非您能标记您所在的位置,否则无法证明地图不是无限的。

http://en.wikipedia.org/wiki/Halting_problem


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接