Excel 中的 Rnd 函数众所周知很弱,而 Excel 中的 RAND 函数基于 Mersenne 算法,并且更强。我一直在尝试寻找 Rnd 的快速且强大的替代方案,并研究了各种选项,包括使用 Mersenne,但这需要大量代码。
另一种选择是从 VBA 调用 Excel RAND 函数,但一次调用一个时速度非常慢。然而,Excel365中的新函数RANDARRAY允许VBA一次性从Excel中调用大量随机数,根据需要使用它们,并在需要时返回更多。这种方法速度快(仅比 Rnd 慢 4 倍,比 Mersenne 代码快)且紧凑 - 代码如下。
我分享这个是希望找到解决这个问题的最佳集体解决方案。
Function RandXL() As Single
Static Remaining As Long, R() As Variant
If Remaining = 0 Then 'get more numbers if necessary
R = Application.WorksheetFunction.RandArray(1000, 1)
Remaining = 1000
End If
RandXL = R(Remaining, 1)
Remaining = Remaining - 1
End Function
我认为通过“质量随机性”,您希望顺序生成的集合中的随机数是独立的。 一些 RNG 在一次取一个数字时表现出色,但每个随机数与其后继数之间具有很强的相关性。 很容易通过 x' = (x + ir) mod 1 生成,其中 ir 是任何无理数,例如 2 的平方根。(您实际上无法在计算机内存中保存无理数,但您可以足够接近 gubbermint 工作。 )事实上,这个 RNG 是如此完美均匀,以至于对于单维积分,通常用于减少方差的蒙特卡洛方法毫无价值。 韦尔定理在这里发挥了重要作用。
数值食谱说要结合使用两种不同的方法。 两种这样的方法是 MWC 和移位 XOR。 这些都很容易实现。 如果您需要较长的时间,您可以通过使用滞后的 MWC 获得类似 2^400,000 的东西。 无论是你还是太阳,都无法持续足够长的时间来耗尽那么多人。 有关详细信息,请在线查看该书,或扫描 George Marsaglia 的论文。
重要的一点:MWC 方法可以提供零,如果您使用 1,这将使您的正态分布函数崩溃。 所以它需要捕获零。 此外,在 MWC 下,所有零并非都相等,因为除了随机数之外,种子的状态还包括进位。 这肯定不是零,否则算法将只提供零,即使在太阳熄灭后也是如此。
不管怎样,我在我的手持式计算器上对这两种方法进行了编程,并对它们进行了明显的测试,它们通过了,但 RANDU 失败了。
您可以使用真正的随机数 - 如我的项目VBA.Random中所示。
它包含 Rnd 的直接替换:
' Returns a true random number as a Double, like Rnd returns a Single.
' The value will be less than 1 but greater than or equal to zero.
'
' Usage: Excactly like Rnd:
'
' TrueRandomValue = RndQrn[(Number)]
'
' Number < 0 -> The same number every time, using Number as the seed.
' Number > 0 -> The next number in the pseudo-random sequence.
' Number = 0 -> The most recently generated number.
' No Number -> The next number in the pseudo-random sequence.
'
' 2019-12-21. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function RndQrn( _
Optional ByVal Number As Single = 1) _
As Double
Static Value As Double
Select Case Number
Case Is > 0 Or (Number = 0 And Value = 0)
' Return the next number in the random sequence.
Value = CDbl(QrnDecimal)
Case Is = 0
' Return the most recently generated number.
Case Is < 0
' Not supported by QRN.
' Retrieve value from RndDbl.
Value = RndDbl(Number)
End Select
' Return a value like:
' 0.171394365283966
RndQrn = Value
End Function
此外,还包含一个演示 (
RandomQrn.xlsm
) 可供下载。
这设置了对 Microsoft Access 16.0 对象库 的引用,其中使用了 Nz 函数。如果您不想使用此参考,可以使用此替代品:
' Replacement for the function Application.Nz() of Access.
'
' 2015-12-10. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function Nz( _
ByRef Value As Variant, _
Optional ByRef ValueIfNull As Variant = "") _
As Variant
Dim ValueNz As Variant
If Not IsEmpty(Value) Then
If IsNull(Value) Then
ValueNz = ValueIfNull
Else
ValueNz = Value
End If
End If
Nz = ValueNz
End Function