我正在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
什么是处理这些周期引入的复杂性的可靠算法方法?
答案 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
方格开始,那么,在一跳后,有:
沿着前两行阅读 - 有一个概率为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之间的数字来表示每个方格上青蛙的比例。所以:
经过多次步骤后,几乎所有青蛙都处于以下三种状态之一:
根据循环的可能性,棘手的部分是决定何时停止模拟。当没有一个权重变化超过一些小值时,我们可以认为我们可以停下来。但是,如果存在一些重复循环,则不会发生这种情况,例如在没有出口的2x1迷宫中,青蛙将无休止地来回跳跃,重量永远不会收敛。对于这项特定任务,假设迷宫的大小有限,您可能会将步数固定为某个“足够大”的值。或者,您可以首先找到所有不可能退出的方块,并将其从收敛测试中排除(如另一个答案所示)。