I then mapped each 32-bit value into [0,1) at double precision to give a 32-bit granularity. I settled on 16MB of floats giving a file size of 128MB and called it, imaginatively, 16MBQNums.dat.
I don't accept anything so did a Fourmilab's Ent test and a PractRand test on the 64MB binary file. I did a bit test with ENT and got a chi-square of 60.58%. PractRand gave a clean sweep.
All I had to do now was to write some code to use 16MBQNums.dat, which takes about 80 milliseconds to load on my machine, and put into an array.
Four procedures are implemented: Sub _Randomize which uses either a cryptographically random entry point into the array or a fixed value; Function Nums which returns a float; GetSnapshot which notes the current array index value and SetSnapshot to load the index got with GetSnapshot.
A Constructor is used which invokes _Randomize and GetSnapshot, so we initialize with a random 'seeding' and a note of the 'seed' used.
Here is a bi file:
QuantumNums.bi
Code: Select all
'#Console On
#define _16MB 16777216
Declare Function MyRandomInt Lib "Advapi32.dll" Alias "SystemFunction036" _
( RandomBuffer As Any Ptr, RandomBufferLength As ULong ) As Byte
Dim Shared _Rnd(1 To _16MB) As Double
Open "16MBQnums.dat" For Binary As #1
Get #1, , _Rnd()
Close #1
Type Q
Declare Constructor
Declare Sub _Randomize( Byval counter As Ulong = 0 )
Declare Function Nums() As Double
Declare Sub GetSnapshot()
Declare Sub SetSnapshot()
ctr As Ulong
ctrSnapshot As ULong
End Type
Private Sub Q._Randomize( Byval counter As Ulong = 0 )
Dim As Ulong dummy
Dim As Any Ptr ptrDummy
If counter > _16MB Then counter = counter Mod _16MB ' Wrap around
If counter = 0 Then
Do
ptrDummy = Varptr( Dummy )
MyRandomInt( ptrDummy, 3 ) ' 3 * 8 => 24-bit ==> 16MB
Loop Until Dummy <> 0
This.ctr = Dummy
Else
This.ctr = counter
End if
End Sub
Private Function Q.Nums() As Double
This.ctr += 1
If This.ctr > _16MB Then This.ctr = This.ctr Mod _16MB
Return _Rnd( This.ctr )
End Function
Private sub Q.GetSnapshot()
This.ctrSnapshot = This.ctr
End Sub
Private Sub Q.SetSnapshot()
This.ctr = This.ctrSnapshot
End Sub
Constructor Q
This._Randomize
This.GetSnapshot
End Constructor
Dim As Q QRnd
#undef Rnd
#defne Rnd QRnd.Nums
Code: Select all
Dim As Q QRnd
#undef Rnd
#define Rnd QRnd.Nums
Here is an example usage: The average float, 0.5000065768629032, is very good given only 16MB of them.
The effective throughput on my machine is about 523MHz. I did not time the raw speed but Q.Nums which has a wrap around check.
Test.bas
Code: Select all
#include "QuantumNums.bi"
Dim As Ulong i
For i = 1 To 4
Print i;" ";QRnd.Nums
Next
For i = 5 To _16MB - 4
QRnd.Nums
Next
Print
For i = _16MB-3 To _16MB + 4
If i = _16MB + 1 Then Print : Print "Wrap around"
Print i;" ";QRnd.Nums
Next
Print
For i = 1 to 1000 ' Burn some numbers
QRnd.Nums
Next
QRnd.GetSnapshot
For i = 1 to 6
Print QRnd.Nums
Next
QRnd.SetSnapShot
Print "Repeat"
For i = 1 to 6
Print QRnd.Nums
Next
Print
Dim As Double tot
For i = 1 To _16MB
tot += QRnd.Nums
Next
Print "Average float:";tot/_16MB
Dim As Double t, x
t = Timer
For i = 1 to _16MB
x = QRnd.Nums
Next
t = Timer - t
Print "Effective throughput";Int(_16MB/(t*10^6));"MHz"
Sleep
Code: Select all
1 0.762950548203662
2 0.7928071254864335
3 0.06614233367145062
4 0.4410213676746935
16777213 0.7405655682086945
16777214 0.8088323173578829
16777215 0.33781035291031
16777216 0.4878995297476649
Wrap around
16777217 0.762950548203662
16777218 0.7928071254864335
16777219 0.06614233367145062
16777220 0.4410213676746935
0.2629275182262063
0.3861238085664809
0.8937501716427505
0.9156534601934254
0.833033949136734
0.2078827200457454
Repeat
0.2629275182262063
0.3861238085664809
0.8937501716427505
0.9156534601934254
0.833033949136734
0.2078827200457454
Average float: 0.5000065768629032
Before I forget here is the file ( 81.1MB zip ): 16MBQNums.