跳转到内容

算法实现/伪随机数/Wichmann-Hill (1982) PRNG

来自维基教科书,开放的书籍,开放的世界

该算法的行为与内置的 VBA Rnd() 函数非常相似,这可能并不奇怪,因为它是 Excel Rand() 函数相当长一段时间的基础。这里给出了一个简短的总结

  • Randomize() 函数用于设置位于模块级别的工作变量。这些变量在每个阶段都用于计算 RndX() 输出,并重新计算它们自己的值,为下次 RndX() 调用做好准备。
  • RandomizeX()没有参数的情况下调用时,使用来自系统计时器的输入来设置变量。当给出参数时,生成的字符输出流将对于该参数是不同的且可重复的。后一种情况适用于没有为 RndX() 提供参数,或者在提供参数的情况下,只要它是一个正数。
  • 最常见的配置,也是最有用的一种配置,是在 RndX() 之前调用 RandomizeX(),两者都没有参数。但是,如果代码中没有包含 RandomizeX()RndX() 在启动时使用的工作变量默认值会导致相应的默认字符输出流。每次在不首先调用 RandomizeX() 的情况下运行 RndX() 时,都会重复此流。
  • RndX() 添加正参数不会播种生成过程。除了 RndX() 的启动默认值之外,所有播种都直接或间接地依赖于代码中是否包含 RandomizeX()
  • 请参阅下面的下拉框,以获取参数设置及其结果的完整表格。
PRNG RndX() 和 RandomizeX() 参数详细信息
RndX() 和 RandomizeX() 参数详细信息
RandomizeX()
参数
RndX()
参数
函数的行为
(假设编码是产生一个序列)
无。 PRNG 流由计算机系统计时器的运行时采样决定。流不确定。
正数 PRNG 流由计算机系统计时器的运行时采样决定。流不确定。RndX() 的正参数根本不影响它。
负数 一个数字,可重复,并且每个数字都不同,并且取决于参数的值。
例如;RndX(-3) 导致 0.05079271
一个数字,可重复,由计算机系统计时器的运行时采样决定;
例如;序列为 0.1741…,01741…
数字2 PRNG 流,可重复,并且每个数字都不同,并且取决于参数的值。
数字 正数 PRNG 流,可重复,并且每个数字都不同,并且取决于参数的值。RndX() 的正参数根本不影响它。
数字 负数 一个数字,可重复,并且每个数字都不同,并且取决于 RndX() 参数的值。RandomizeX() 参数值完全没有影响。
例如;RndX(-51) 导致 0.8634…
数字 一个数字,可重复,并且每个数字都不同,并且取决于参数的值。
例如;RandomizeX(2346) 导致 0.2322…
函数
未使用
默认 PRNG 流,可重复,并且始终相同。
例如;序列为 0.8952…,0.1114…,0.9395…
函数
未使用
正数 默认 PRNG 流,可重复,并且始终相同。
例如;序列为 0.8952…,0.1114…,0.9395…
函数
未使用1
负数
或零
一个数字,可重复,并且每个数字都不同,并且取决于参数的值。
例如;RndX(0) = 0.8694...: -5 = 0.0846…


1. 术语函数未使用意指用户没有在代码中专门调用该函数。在某些情况下,例如这种情况,RandomizeX() 函数仍然需要对RndX() 函数本身可用。
2. 数字项是可以转换为数字的项。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
华夏公益教科书