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.

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.

The author of MsWs provides 25000 values of s; 2^14.60964047 values.

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