题目
如下图所示,在一个正方体的铁丝骨架上有只小虫在爬行。小虫从A点出发想要爬到G点,但是由于铁丝非常丝,而小虫又看不清楚路,所以小虫的爬行有两个特点:
- 从铁丝架上的一点到另一点时的过程中不会掉头返回。如从A点至B点,在到达B点前不会掉头;
- 在某一点向其他方向出发时,选择的方向完全是随机的。如在A点有BDE三个方向,每个方向选择的概率都是三分之一。
问题:小虫平均要经过几个点才能到达点G?
分析
如果直接使用数学计算,我们大概可以使用以下的公式进行计算:
其中,
表示平均步数的期望值,
表示步长,
表示步长为n 的组合。由于至少要3步才能到达G,所以
的下限为3。由于每长一步,概率会变为原来的
,所以系数为
。举例来说,当
时,系数为
,而所有的组合有6种:[ABCG, ABFG, ADCG, ADHG, AEFG, AEHG],所以步长为
。
由于 由于 是常数,而 是比 高阶的无穷大,所以极限收敛。所以结果只可以计算, 为一个定值。然而,由于无法进行每个n的组合的计算,所以很难使用数学方法进行直接云计算。因此,我们利用计算机的高速进行模拟,则可以得出大概次数。
算计设计
- 总体思路
设计一个选择函数,模拟小虫的一次路径的选择,然后模拟一次小虫子走过的路径,最后进行大量的实验,再求出平均值。 - 具体实现
首先,使用一个二维数组表示连接关系,使用0-7分别对应A-G,我们可以得到以下的二维数组。
static int[][] directions = {
{ 1, 3, 4 },
{ 0, 2, 5 },
{ 1, 6, 3 },
{ 0, 2, 7 },
{ 0, 5, 7 },
{ 1, 4, 6 },
{ 2, 5, 7 },
{ 3, 4, 6 } };
这样, directions[0]
表示所对应的一维数组在A点可以选择的三个方向为1,3,4,即A,D,E。以此类推,然后使用随机函数,从三个中选择一个即可。详细代码见附录。
测试
我们使用StringBuilder记录中间的过程,通过在主函数中调用 test(10)
,我们可以得到一个结果如下所示。
Steps: 15 Path: ADHEAEFEAEAEFEFG
Steps: 7 Path: ABABCDHG
Steps: 3 Path: ABFG
Steps: 25 Path: ABAEHDCBCBABADCDADHDHDHDHG
Steps: 3 Path: AEHG
Steps: 15 Path: ABABFEADCBFEHEFG
Steps: 7 Path: ABABFBCG
Steps: 5 Path: AEFBCG
Steps: 3 Path: AEFG
Steps: 3 Path: ABFG
结果与预期的一致。
结论
现在执行附录中的源代码中的主函数,可以得到最终结果。
Average steps: 10.0164
即,平均的步数是10步,去除A点和G点,平均要经过的点是8个。
附录:源代码
public class WormSimulation {
// 方向选择数组
static int[][] directions = {
{ 1, 3, 4 },
{ 0, 2, 5 },
{ 1, 6, 3 },
{ 0, 2, 7 },
{ 0, 5, 7 },
{ 1, 4, 6 },
{ 2, 5, 7 },
{ 3, 4, 6 } };
public static void main(String[] args) {
int totalSteps = 0; // 小虫总步长。
int totalTimes = 10000; // 测试10000次。
for(int i = 0; i < totalTimes; i++)
totalSteps+= runOnce(); // 累加每次测试爬过的步数。
System.out.println("Average steps: " + (1.0*totalSteps/totalTimes));
}
// 模拟一次小虫从A到G的过程,返回总用步长。
static int runOnce() {
int p = 0;
int steps = 0;
while (p != 6) {
p = directions[p][(int) (Math.random() * 3)];
steps++;
}
return steps;
}
static void test(int times) {
for (int i = 0; i < times; i++) {
int p = 0;
int steps = 0;
StringBuilder sb = new StringBuilder();
sb.append("A");
while (p != 6) {
p = directions[p][(int) (Math.random() * 3)];
sb.append((char) (p + 65));
steps++;
}
System.out.println("Steps: " + steps + "\tPath: " + sb.toString());
}
}
}