算法实现/伪随机数/Wichmann-Hill (1982) PRNG
外观
Visual Basic
[编辑 | 编辑源代码]该算法的行为与内置的 VBA Rnd() 函数非常相似,这可能并不奇怪,因为它是 Excel Rand() 函数相当长一段时间的基础。这里给出了一个简短的总结
- Randomize() 函数用于设置位于模块级别的工作变量。这些变量在每个阶段都用于计算 RndX() 输出,并重新计算它们自己的值,为下次 RndX() 调用做好准备。
- RandomizeX() 在没有参数的情况下调用时,使用来自系统计时器的输入来设置变量。当给出参数时,生成的字符输出流将对于该参数是不同的且可重复的。后一种情况适用于没有为 RndX() 提供参数,或者在提供参数的情况下,只要它是一个正数。
- 最常见的配置,也是最有用的一种配置,是在 RndX() 之前调用 RandomizeX(),两者都没有参数。但是,如果代码中没有包含 RandomizeX(),RndX() 在启动时使用的工作变量默认值会导致相应的默认字符输出流。每次在不首先调用 RandomizeX() 的情况下运行 RndX() 时,都会重复此流。
- 为 RndX() 添加正参数不会播种生成过程。除了 RndX() 的启动默认值之外,所有播种都直接或间接地依赖于代码中是否包含 RandomizeX()。
- 请参阅下面的下拉框,以获取参数设置及其结果的完整表格。
|
Option Explicit
Dim nX As Long, nY As Long, nZ As Long
Sub TestRndX()
Dim n As Long
RandomizeX
For n = 1 To 1000
'Debug.Print RndX()
MsgBox RndX()
Next n
End Sub
Sub RandomizeX(Optional ByVal nSeed As Variant)
'sets variables for PRNG procedure RndX()
Const MaxLong As Double = 2 ^ 31 - 1
Dim nS As Long
Dim nN As Double
'make multiplier
If IsMissing(nSeed) Then
nS = Timer * 60
Else
nN = Abs(Int(Val(nSeed)))
If nN > MaxLong Then 'no overflow
nN = nN - Int(nN / MaxLong) * MaxLong
End If
nS = nN
End If
'update variables
nX = (nS Mod 30269)
nY = (nS Mod 30307)
nZ = (nS Mod 30323)
'avoid zero state
If nX = 0 Then nX = 171
If nY = 0 Then nY = 172
If nZ = 0 Then nZ = 170
End Sub
Function RndX(Optional ByVal nSeed As Long = 1) As Double
'PRNG - gets pseudo random number - use with RandomizeX
'Wichmann-Hill algorithm of 1982
Dim nResult As Double
'initialize variables
If nX = 0 Then
nX = 171
nY = 172
nZ = 170
End If
'first update variables
If nSeed <> 0 Then
If nSeed < 0 Then RandomizeX (nSeed)
nX = (171 * nX) Mod 30269
nY = (172 * nY) Mod 30307
nZ = (170 * nZ) Mod 30323
End If
'use variables to calculate output
nResult = nX / 30269# + nY / 30307# + nZ / 30323#
RndX = nResult - Int(nResult)
End Function
- Wichmann, Brian; Hill, David (1982), Algorithm AS183: An Efficient and Portable Pseudo-Random Number Generator, Journal of the Royal Statistical Society. Series C