【发布时间】:2019-06-21 15:01:00
【问题描述】:
在尝试解决this question 时,我编写了以下代码以尝试实现Box-Muller transform 以在纯VBA 中生成随机正态变量:
Function RandNorm(Optional mean As Double = 0, Optional sd As Double = 1) As Double
Dim s As Double
s = Sqr(-2 * Log(Rnd())) * Cos(6.283185307 * Rnd()) '6.28 etc. is 2*pi
RandNorm = mean + sd * s
End Function
以下有点弱的测试总是有效,返回一个接近 0 的数字:
Sub test1()
Randomize
Dim s As Double
Dim i As Long
For i = 1 To 17000000
s = s + RandNorm()
Next i
Debug.Print s / 17000000
End Sub
另一方面,以下测试 从不 起作用(因为它试图获取未定义的 0 的日志):
Sub test2()
Randomize
Dim s As Double
Dim i As Long
Debug.Print Rnd() 'just to clock it
For i = 1 To 17000000
s = s + RandNorm()
Next i
Debug.Print s / 17000000
End Sub
问题在于,rnd() 平均每 2^24 次(略少于 17,000,000 次)调用返回一次 0。当然很容易调整RandNorm 的定义以避免零(参见链接到的问题),但我仍然对上面的代码感到困惑。如果每个测试有一半时间失败(当零被输入Log())并且一半时间工作(当零被输入Cos()),这对我来说非常有意义。似乎 Randomize 至少避免了一半可能的种子。
为什么 Randomize 会这样?有没有办法播种随机数生成器,以便随机数生成器的所有可能状态都可以发生?
编辑时
如果我定义以下子:
Sub ReRandomize()
Dim r As Double
Randomize
If Rnd() > 0.5 Then r = Rnd()
End Sub
并修改上面的test1 和test2 以使用ReRandomize 而不是Randomize,这两个测试潜艇将有50% 的时间失败,以便可能回答这部分关于是否有“一种方法来播种随机数生成器,以便随机数生成器的所有可能状态都可以发生”的问题? Randomize 的行为方式仍然是个谜。这是 Excel VBA 问题第二次让我意识到Randomize is a weird sub。对于rnd() 的典型用法,这些都不是很重要,但它确实强调了它是一个质量有点低的随机数生成器,不应该用于严肃的统计工作。
【问题讨论】:
-
有趣的是,在调用 Randomize 之后,零 - 从不 - 似乎是 random 返回的第一个值。此外,正如您所指出的,零值仅出现在 #ofTimeRndCallAfterRandomize MOD 2 = 0 的情况下。如果您在每次调用 Rnd() 之前调用 Randomize,则不会生成任何 0。通过在 test2 中调用 Rnd(),您可以强制这种不良行为仅生成要提供到 log 中的零,因为它总是调用 rnd 的第二次迭代,而在第一次测试中,cos 总是获得零侧状态。
-
这似乎取决于我们在 Debug.Print 行中用于 Rnd() 的种子,但我不知道为什么
-
不确定这是否是您所要求的,但请尝试将呼叫转移到
Test2中的RandomizeafterDebug.Print Rnd()。这应该可以完成(至少对我来说是这样!),我认为当您在Randomize之后调用Rnd()时,种子会发生一些变化