使用蒙特卡洛技术解决一个小虫爬铁丝问题。

题目

如下图所示,在一个正方体的铁丝骨架上有只小虫在爬行。小虫从A点出发想要爬到G点,但是由于铁丝非常丝,而小虫又看不清楚路,所以小虫的爬行有两个特点:

  • 从铁丝架上的一点到另一点时的过程中不会掉头返回。如从A点至B点,在到达B点前不会掉头;
  • 在某一点向其他方向出发时,选择的方向完全是随机的。如在A点有BDE三个方向,每个方向选择的概率都是三分之一。

问题:小虫平均要经过几个点才能到达点G?
在这里插入图片描述

分析

如果直接使用数学计算,我们大概可以使用以下的公式进行计算:

E N = lim n + 3 n ( 1 3 ) n n p n = lim n + 3 n n 3 n p n E_N = \lim_{n \rightarrow +\infty} \sum^{n}_{3} (\frac{1}{3})^nnp_n = \lim_{n \rightarrow +\infty} \sum^{n}_{3} \frac{n}{3^n}p_n
其中, E N E_N 表示平均步数的期望值, n n 表示步长, p n p_n 表示步长为n 的组合。由于至少要3步才能到达G,所以 n n 的下限为3。由于每长一步,概率会变为原来的 1 3 \frac{1}{3} ,所以系数为 ( 1 3 ) 3 (\frac{1}{3})^3 。举例来说,当 n = 3 n = 3 时,系数为 1 27 \frac{1}{27} ,而所有的组合有6种:[ABCG, ABFG, ADCG, ADHG, AEFG, AEHG],所以步长为 ( 1 3 ) 3 3 8 = 8 9 (\frac{1}{3})^3*3*8 = \frac{8}{9}

由于 由于 p n p_n 是常数,而 3 n 3^n 是比 n n 高阶的无穷大,所以极限收敛。所以结果只可以计算, E N E_N 为一个定值。然而,由于无法进行每个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());
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_43145361/article/details/88743114
今日推荐