【问题标题】:Randomize seems to miss many possible seeds随机化似乎错过了许多可能的种子
【发布时间】: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

并修改上面的test1test2 以使用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 中的Randomize after Debug.Print Rnd()。这应该可以完成(至少对我来说是这样!),我认为当您在 Randomize 之后调用 Rnd() 时,种子会发生一些变化

标签: vba random


【解决方案1】:

我只是将 Rnd calc 修改为不包含 0 或 1。您必须记住,Rnd 函数可以产生 0 或 1 范围内的数字(双精度类型)。因此,有可能出现重复数字相当低。

    dbl1stRnd = Rnd()
    dblRnd = (0.9999 - 0.0001) * dbl1stRnd + 0.0001

    s = Sqr(-2 * Log(dblRnd)) * Cos(6.283185307 * dblRnd) '6.28 etc. is 2*pi

带有随机化的常规 Rnd() 函数的一些示例输出:

 3.633606E-02 
 0.2324036 
 0.3460443 
 0.5870923 
 5.553758E-02 
 0.2629338 
 0.2400494 
 0.1982901 
 0.5923058 
 0.7915452 
 0.4874671 
 0.2062811 
 0.5676001 
 0.1178594 
 1.932621E-03 
 0.4326598 
 0.8291379 

我希望这可以解释一些,并且是您正在寻找的。​​p>

【讨论】:

  • 问题不在于如何避免0(这很容易防范),而在于Randomize 发生了什么。如果在调用Randomize 后不能直接出现随机数生成器可能状态的一半(甚至更多)(似乎是这种情况),那么使用Randomize 可能会导致微妙的偏差蔓延进入计算。
  • 我知道您对0 没有任何问题。但是,我所说的是 randomize 生成一个从 0 到 1 的数字(双精度数)。因此,它不会产生重复(很可能)。我发布了 Rnd 的一些示例输出。
猜你喜欢
  • 2016-02-25
  • 2014-01-08
  • 1970-01-01
  • 1970-01-01
  • 2019-02-18
  • 2012-03-30
  • 2012-02-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多