使用周期

时间:2018-03-26 21:00:42

标签: algorithm computer-science graph-theory markov-chains

我正在this programming challenge工作,青蛙随机走过一个迷宫,沿着潜在的出口前往各种障碍物和炸弹。

挑战是计算青蛙到达出口的概率。

我的问题是,我不知道如何处理周期 - 青蛙可以在两个或多个空间之间来回走动的情况。

想象一下你有:

     BOMB    BOMB
EXIT SPACE 1 SPACE 2 BOMB
     BOMB    BOMB

对于空间1,进入出口的概率是直接走到出口(1/4)的概率,或者来回走直到最终到达出口(1/4 ^ 3 + 1/4) ^ 6 + 1/4 ^ 9 ......)。对于空间2(1/4 ^ 2 + 1/4 ^ 5 ......)

如果您有多个空闲空间,例如

,这会更加混乱
     BOMB    BOMB    BOMB
EXIT SPACE 1 SPACE 2 SPACE 3 BOMB
     BOMB    BOMB    BOMB

什么是处理这些周期引入的复杂性的可靠算法方法?

3 个答案:

答案 0 :(得分:3)

我会分两个阶段解决这个问题。

第一阶段是确定您可以以任何方式退出哪个方格。这将让你找到并识别出“你被卡住”任何没有可能出口的闭环。

完成分析后,您可以为所有死角和炸弹分配0,为所有退出分配1。退出所有其他正方形的概率将是一组线性方程的唯一解,其中p(i, j) = average(p(i', j')可以在一个回合中移动所有位置。这将是n x m变量中的一组n x m方程式。使用您最喜欢的线性代数技术解决这个问题(我建议减少行)。

现在,对于每个方格,您都知道能够退出的确切概率。现在你的答案很简单。

请注意,如果您只是尝试第二种方法的线性代数部分,则线性方程组的解决方案将不是唯一的。第一阶段解决了这个问题,以确保您提出正确的解决方案。

答案 1 :(得分:1)

迷宫:

+----+----+----+----+----+
|    |BOMB|BOMB|BOMB|    |
+----+----+----+----+----+
|EXIT| S1 | S2 | S3 |BOMB|
+----+----+----+----+----+
|    |BOMB|BOMB|BOMB|    |
+----+----+----+----+----+

您可以将其映射到每次移动后发生的概率矩阵:

//    DEAD, ESC,  S1,   S2,   S3
p = [
    [ 1.00, 0.00, 0.00, 0.00, 0.00 ], // DEAD 
    [ 0.00, 1.00, 0.00, 0.00, 0.00 ], // ESCAPED
    [ 0.50, 0.25, 0.00, 0.25, 0.00 ], // S1 
    [ 0.50, 0.00, 0.25, 0.00, 0.25 ], // S2 
    [ 0.75, 0.00, 0.00, 0.25, 0.00 ]  // S3
];

所以,沿着中间一行阅读,如果青蛙在S1方格开始,那么,在一跳后,有:

  • 概率为0.5;
  • 0.25概率将逃脱;和
  • 0.25概率将在S2。

沿着前两行阅读 - 有一个概率为1,如果它已经死亡/逃脱,那么它将分别保持死亡/逃脱。

要计算n跳后的概率,你只需将概率矩阵提高到幂n

//    DEAD, ESC,  S1,   S2,   S3
p = [
    [ 1.00, 0.00, 0.00, 0.00, 0.00 ], // DEAD 
    [ 0.00, 1.00, 0.00, 0.00, 0.00 ], // ESCAPED
    [ 0.50, 0.25, 0.00, 0.25, 0.00 ], // S1 
    [ 0.50, 0.00, 0.25, 0.00, 0.25 ], // S2 
    [ 0.75, 0.00, 0.00, 0.25, 0.00 ]  // S3
    ];

function multiply( a, b )
{
  if ( a[0].length !== b.length )
  {
    throw "Matrices must have same a.x/b.y dimensions!";
  }
  let r = [];
  for ( let y = 0; y < a.length; y++ )
  {
    r[y] = [];
    for ( let x = 0; x < b[0].length; x++ )
    {
      r[y][x] = 0;
      for ( let i = 0; i < a[0].length; i++ )
      {
        r[y][x] += a[y][i] * b[i][x];
      }
    }
  }
  return r;
}

// Start at S1
r = [ [ 0, 0, 1, 0, 0 ] ];
// Output probabilities up to 10 decimal places.
dp = 10;

console.log(
  "i"
  + " " + "DEAD".padEnd(" ",dp+2)
  + " " + "ESCAPED".padEnd(" ",dp+2)
  + " " + "S1".padEnd(" ",dp+2)
  + " " + "S2".padEnd(" ",dp+2)
  + " " + "S3".padEnd(" ",dp+2)
);
for ( let i = 1; i <= 50; i++ ){
  r = multiply( r, p );
  if ( i < 10 || i % 10 === 0 )
  {
    console.log(
      i
      + " " + Number(r[0][0]).toFixed(dp)
      + " " + Number(r[0][1]).toFixed(dp)
      + " " + Number(r[0][2]).toFixed(dp)
      + " " + Number(r[0][3]).toFixed(dp)
      + " " + Number(r[0][4]).toFixed(dp)
    );
  }
}

这个简单的例子表明,它迅速收敛到一种状态,即青蛙不太可能仍然在移动,并且0.7321428571有可能死亡,0.2678571429它是活着。

挑战在于获取输入图并输出矩阵,然后查看优化方法,将矩阵提升为幂以快速找到此收敛点(通过重复平方矩阵 - p = multiply(p, p); - 或other methods)。

答案 2 :(得分:0)

建议求解一组线性方程的答案是正确的,但这是另一种方法。

想象一下,大量的青蛙都被放置在起始广场上并开始在迷宫周围随意移动。青蛙们同时迈出了一步。在每个时间步,我们可以使用0.0到1.0之间的数字来表示每个方格上青蛙的比例。所以:

  • 在0时,所有的青蛙都在开始,所以方块的重量为1.0,其余的重量为0.0
  • 每个时间步骤:
    • 炸弹上的任何青蛙都被摧毁(设定重量为0.0)
    • 任何到达出口的青蛙都呆在那里
    • 任何其他广场上的青蛙均匀分布在其邻居之间
    • 所有青蛙同时移动,因此需要一次性执行这些更新

经过多次步骤后,几乎所有青蛙都处于以下三种状态之一:

  1. 被炸弹摧毁
  2. 在出口处等候
  3. 在无限循环中迷失在迷宫中
  4. 根据循环的可能性,棘手的部分是决定何时停止模拟。当没有一个权重变化超过一些小值时,我们可以认为我们可以停下来。但是,如果存在一些重复循环,则不会发生这种情况,例如在没有出口的2x1迷宫中,青蛙将无休止地来回跳跃,重量永远不会收敛。对于这项特定任务,假设迷宫的大小有限,您可能会将步数固定为某个“足够大”的值。或者,您可以首先找到所有不可能退出的方块,并将其从收敛测试中排除(如另一个答案所示)。