The MsWs drop in Function has an issue. This was created when we 'hard-wired' s; where s should be non-zero in the upper 32 bits and 1 in the least significant bit to add to the stepping sequence w += s.
The author of MsWs provides 25000 values of s; 2^14.60964047 values.One might think that non-overlapping data would always be obtained if the generator was started with different x values but the same w and s values. It is possible for two different x values to have the same result mod 2^64 after squaring. Should this happen, exactly the same data would be produced even though the initial x values were different. To guarantee distinct data through the period of 2^64, one should use a different s value for each run. This will produce different data and prevent any occurrence of overlapping data.
Note that we are referring to the same w and s. We are using a 'hard-wired' s so all we need do is ensure that we do not use the same w and avoid trouble that way. However, if we use a random w it is possible to generate the same w. A random w is got by using the function Get64Bit. The chance of a 'collision' is then 2^64 to 1 against which is considerably larger than 2^14.60964047. The odds of winning the US Powerball lottery is 292,201,338 to 1 against; approximately 2^28.12238754. 2^64 is greater than 2^35 x 2^28.12238754 so 2^64 is treated as 'it ain't going to happen'. <smile>
With the function MyRandomize the option MyRandomize( 0, <fixed w> ) would generate a random seed and that plus <fixed w> will lead to overlapping data. If you use MyRandomize( 0, <fixed w> ) you will not get it but will get, instead, MyRandomize(). We can use <fixed w> but only if we use <fixed seed> as well when we require to repeat a sequence.
MyRandomiize() is invoked via a Constructor so if you require full random 'seeding' then there is no need to employ MyRandomize. MyRandomize includes a small warmup. On my machine, MyRandomize takes 3.4ms to complete.
The following code has nothing like the features of PCG32II, which has been worked upon for some time, and currently only two generator functions are available namely Rand ( Ulongint ) and RandSE.
However, MsWs has more to it than meets the eye - the giveaway is in the last paragraph. With a Ulongint output, only one random number is required to give 53-bit granularity for [0,1) coming in, using badidea's loop overhead filter, at 433MHz. PCG32II, on the other, requires two Ulongs coming in at 221MHz. CryptoRNDII requires two Ulongs coming in at 442MHz. Knuth64, MsWs nearest competitor, comes in at 487MHz. Knuth64 fails PractRand at 8KB which does not compare favorably with MsWs 1TB success. In 'real life' situations the difference between 433MHz and 487MHz will not be remarkable. All of those figures are based upon 9 runs each and the median taken for FBC 1.05/gcc 5.2 64-bit. If thread safety is an issue then MsWs is 'top dog'.
I have just run the test code and got an average of 0.4999956925552035. An accuracy of five significant figures is not unusual for MsWs.
MsWs.bas ( Updated to include Range functions: 29 Oct 2018 3rd revision; 30 Oct 2018 4th revision, see MsWsParams.Range; 01 Nov 2018 5th revision, corrected a blunder in MsWsParams.Range)
Code: Select all
#define Get64Bit Cast( Ulongint, Rnd*2^64 )
Type MsWsParams
Declare Constructor
Declare Sub MyRandomize( Byval seed As Ulongint = 0, Byval sequence As Ulongint = 0 )
Declare Function Rand() As Ulongint
Declare Function RandSE() As Double
Declare Function Range Overload ( First as Double, Last as Double ) as Double
Declare Function Range Overload ( First as Longint, Last as Longint ) as Longint
Seed As Ulongint
Sequence As Ulongint
End Type
Sub MsWsParams.MyRandomize( Byval seed As Ulongint = 0, Byval sequence As Ulongint = 0 )
Randomize , 5
If seed = 0 Then
This.Seed = Get64Bit
This.Sequence = Get64Bit
Else
If sequence = 0 Then
This.Seed = seed
This.Sequence = Get64Bit
Else ' For a sequence repeat
This.Seed = seed
This.Sequence = sequence
End If
End If
' Warm up generator - essential for any PRNG
For i As Ulong = 1 To 1000
This.rand()
Next
End Sub
' 18446744073709551557 is the largest prime number < 2^64
Function MsWsParams.Rand() As Ulongint
This.Seed *= This.Seed : This.Sequence += 18446744073709551557 : This.Seed += This.Sequence
This.Seed = ( This.Seed Shr 32 ) Or ( This.Seed Shl 32 )
Return( This.Seed )
End Function
Function MsWsParams.RandSE() As Double
This.Seed *= This.Seed : This.Sequence += 18446744073709551557 : This.Seed += This.Sequence
This.Seed = ( This.Seed Shr 32 ) Or ( This.Seed Shl 32 )
Return This.Seed/2^64
End Function
Function MsWsParams.Range( First as Double, Last as Double ) as Double
This.Seed *= This.Seed : This.Sequence += 18446744073709551557 : This.Seed += This.Sequence
This.Seed = ( This.Seed Shr 32 ) Or ( This.Seed Shl 32 )
Function = This.Seed/2^64*( Last - First ) + First
End Function
' Following function from code by dafhi
Function MsWsParams.Range(First As Longint, Last As Longint) As LongInt
Function = This.Seed/2^64 * (Last - First + 1) - .5 + First
End Function
Constructor MsWsParams
This.MyRandomize()
End Constructor