在Excel VBA中找到Rnd的良好替代品

问题描述

众所周知,Excel中的Rnd函数较弱,而Excel中的RAND函数则基于Mersenne算法且功能更强大。我一直在尝试找到一种快速而强大的替代Rnd的方法,并研究了各种选择,包括使用Mersenne,但这需要大量的代码

解决方法

另一种选择是从VBA调用Excel RAND函数,但是一次执行一次则非常慢。但是,Excel365中的新函数RANDARRAY允许VBA一次调用Excel中的大量随机数,根据需要使用它们,并在必要时返回更多。这种方法速度快(仅比Rnd慢4倍,比Mersenne代码快4倍)且结构紧凑-代码如下。

我正在分享这个信息,希望找到解决此问题的最佳集体解决方案。

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
,

您可以使用真正的随机数-如我的项目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,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