Algorithm Implementation/Pseudorandom Numbers/Wichmann-Hill (1982) PRNG
This algorithm has behavior that is broadly similar to that of the built-in VBA Rnd() function, and this is perhaps not surprising since it was the basis of the Excel Rand() function for quite some time. A short summary is given here:
- The Randomize() function is used to set the working variables located at module level. In turn, these variables are used at every stage to calculate RndX() outputs and to re-calculate their own values, ready for the next RndX() call.
- RandomizeX() sets variables using an input from the system timer when it is called without a parameter. When given a parameter, the output stream of characters generated will be distinct and repeatable for that parameter. This latter state applies when no parameter for RndX() is given, or if given, provided that it is a positive number.
- The most common configuration, and most useful, calls RandomizeX() before RndX(), both without parameters. However, if RandomizeX() is not included in the code, the work variable defaults that are used by RndX() at startup lead to a corresponding default output stream of characters. This same stream is repeated whenever RndX() is run without first calling RandomizeX().
- Adding positive parameters for RndX() will not seed the generation process. Apart from the starup defaults for RndX(), all seeding depends directly or indirectly on having RandomizeX() in the code.
- Refer to the drop box below for a complete tabulation of the parameter settings and their outcomes.
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