用excel VBA模拟醉汉走路过程
李永乐老师曾经讲过一个“醉汉走路”问题。这个问题常被用来解释布朗运动※。说一个醉汉,已经意识模糊,但任然可以走路。只不过,他走路的方向是完全随机的。那么经过一定的步数以后,醉汉是倾向于原地打转,还是离开起点一定距离呢?
※布朗运动指的是极小的微粒(比如一粒花粉),受到周围众多处于无序热运动中的粒子(比如分子)扰动,“被迫”进行无序的运动
答案是,醉汉倾向于离开起点一定距离,而不是原地打转。具体的请大家移步李永乐老师的频道。在这里,我只想介绍一种用excel及其附带的VBA工具,来模拟“醉汉走路”过程的方法。
首先,舞台在excel的表格上。也就是说,最小的单元就是一个单元格。每一次移动就是随机地移动到相邻的8个单元格中的其中1个(移动到斜对角也算1格)。限定的步数为100步。所以,为了有足够大的空间,把起始位置设定到坐标为(101,101)※的单元格上,把它用黄色边框标记。这样即使出现始终往一个方向运动的情况也不会超出范围。
※VBA里描述单元格坐标的格式为(行序号,列序号)。如第3行,B列就是(3,2)
然后,每到达一个单元格,就让其中显示数字,记录到达过的次数※。同时也为这些单元格上色,到达次数越多颜色就越深。最后给终点标记上红色。
※起点在一开始时不会被计数,但之后会正常计数。所以起点格中的数字代表返回起点的次数

上图显示了1次运行的结果。可以看出,这次运动没有返回过起点,在上方的一片区域(数字为4,5,6)盘旋了多次,最后位置在右下方的一个曾经到达过的位置。因为是随机※的运动,每一次运行的结果会不一样。当然,步数也可以被设置为更大的值,只不过要将起点设置得远一些以获得更大的舞台。下面是代码。
※像VBA这样弱小的编程工具里是不存在真随机的,这里只是简单模拟
Sub walk2()
Dim x As Integer
Dim y As Integer
Dim i As Integer
Dim temp As Double
Range(Cells(1, 1), Cells(200, 200)).Interior.Pattern = xlNone
Range(Cells(1, 1), Cells(200, 200)).ClearContents
‘清除范围内的底色和数值
x = 101
y = 101
‘设置起点坐标。这里y代表行,x代表列
For i = 1 To 100
temp = 8 * Rnd
If temp < 1 Then
y = y - 1
ElseIf temp >= 1 And temp < 2 Then
x = x + 1
y = y - 1
ElseIf temp >= 2 And temp < 3 Then
x = x + 1
ElseIf temp >= 3 And temp < 4 Then
x = x + 1
y = y + 1
ElseIf temp >= 4 And temp < 5 Then
y = y + 1
ElseIf temp >= 5 And temp < 6 Then
x = x - 1
y = y + 1
ElseIf temp >= 6 And temp < 7 Then
x = x - 1
ElseIf temp >= 7 And temp <= 8 Then
x = x - 1
y = y - 1
End If
‘获取1个0~8的随机数,根据数值决定下一格的位置。重复100次
Cells(y, x).Value = Cells(y, x).Value + 1
‘给到达过的格子计数
Cells(y, x).Interior.Color = RGB(255 - 17 * Cells(y, x).Value, 255 - 17 * Cells(y, x).Value, 255 - 17 * Cells(y, x).Value)
‘给到达过的格子上色
Next i
Cells(y, x).Interior.Color = RGB(255, 0, 0)
‘给终点格上红色
End Sub
当然,这个运行过程相当快。如果想要看到过程的话,需要在每一步之间加入延时。VBA并没有自带的延时,需要自己写一个。方法有很多,我提供一个以获取系统时间的方式实现的代码。可以实现1秒的延时。
Sub delay1000()
Dim t1 As Single
t1 = Timer
Do
DoEvents
Loop While Timer - t1 < 1
End Sub
用“call delay1000”这个语句调用延时,加在之前的“Next i”的上一句就行了。
然后,为了可视化效果更好一点,在加数字和上色的同时,还可以令单元格被选中。用“cells(y,x).select”这个语句。
运行的效果我已经录成视频了。

最后,我想用这种方式来计算运动100步后,终点与起点距离的数学期望的近似值※。只不过,因为前面把移动到斜对角也看作1步了,所以计算距离的方法需要做些改变,以最小的格子数来表示。
※也就是多次运行结果的平均值。运行次数越多就越逼近数学期望
如果初始坐标是(a,b),终点坐标是(x,y)。那么距离就是:
Max(Abs(a-x),Abs(b-y))
比如下面这个例子,行方向上相距3,列方向上相距7,所以距离就是较大的7 。

因为不需要显示出来,所以可以略去部分代码。再加上一层循环,以及计算最终结果部分。代码如下。
Sub walk5()
Dim x As Integer
Dim y As Integer
Dim i As Integer
Dim j As Integer
Dim temp As Double
Dim d As Double
For j = 1 To 10000
x = 101
y = 101
For i = 1 To 100
temp = 8 * Rnd
If temp < 1 Then
y = y - 1
ElseIf temp >= 1 And temp < 2 Then
x = x + 1
y = y - 1
ElseIf temp >= 2 And temp < 3 Then
x = x + 1
ElseIf temp >= 3 And temp < 4 Then
x = x + 1
y = y + 1
ElseIf temp >= 4 And temp < 5 Then
y = y + 1
ElseIf temp >= 5 And temp < 6 Then
x = x - 1
y = y + 1
ElseIf temp >= 6 And temp < 7 Then
x = x - 1
ElseIf temp >= 7 And temp <= 8 Then
x = x - 1
y = y - 1
End If
Next i
If Abs(x - 101) >= Abs(y - 101) Then
d = d + Abs(x - 101) / 10000
Else
d = d + Abs(y - 101) / 10000
End If
Next j
MsgBox d
End Sub
我运行了几次,基本上是在9.7~9.8之间。与布朗运动的规律是接近的。布朗运动的期望是步数的平方根。运动100步,距离的期望就是10步。运动10000步,距离的期望就是100步。你也可以copy这个代码,将步数(i的值)修改成10000,看看结果会不会是100附近(运行起来可能有点费时)。

