In FB's Help file under Rnd we have rnd_range to give a random double in the range first to last as doubles. For a random integral number we are given a method which utilizes rnd_range.
Presented here is an overloaded rnd_range to give a random integral number using dodicat's Clng/Mod method which is twice as fast.
The code below uses 'Randomize 666' and then prints FB's value, and then we 'Randomize 666' again and then print dodicat's value. We get 305 and 136 respectively. This may seem a worry, being different, but the FB method is a linear mapping whereas dodicat's method is not; dodicat calls it a 'local range mapper, local being first and last'. I should like to come up with a better description.
The important thing is not the value of a single call but that the two methods should give, when calling many times, the same average. The next part of the code does just that and averages 10^8 calls.
It is worth noting that the FB average is 0.5 less than dodicat's average. The reason for that is because FB uses 'Int(rnd_range(first, last))' which is wrong - it should be 'Int(rnd_range(first, last)) + 0.5'.
The averages without correcting FB were 244.49028122 and 245.01891906 with the real average being 245.
Additional tests were done using a variety of first and last with negatives and so on but dodicat's method got a thorough testing before introducing it to PCG32II.
I have some code for timing random numbers which extracts the influence of a For/Next loop normally used for testing. FB's throughput was found to be 35MHz whereas dodicat's method had a throughput of 70MHz, twice as fast as FB's method.
So, if you have been using FB's 'Int(rnd_range(first, last))', even though it is wrong, give dodicat's method a try. He would like royalties but this is FreeBASIC so he will have to settle with the fame for a very ingenious method.
Code: Select all
'' Function to a random number in the range [first, last), or {first <= x < last}.
Function rnd_range overload (first As Double, last As Double) As Double
Function = Rnd * (last - first) + first
End Function
'' Implements dodicat's Clng/Mod method
Function rnd_range overload (first As Long, last As Long) As Long
Function = CLng( cast( Ulong, Rnd*2^32 ) Mod (last - first + 1) ) + first
End Function
Randomize 666
Print Int(rnd_range(69d, 421d)) ' FB
Randomize 666
Print rnd_range(69l, 421l) ' dodicat
Dim As Long i
Dim As Double tot
Randomize
Print
For i = 1 To 10^8
tot += Int(rnd_range(69d, 421d)) ' FB
Next
tot /= 10^8
Print tot
tot = 0
For i = 1 To 10^8
tot += rnd_range(69l, 421l) ' dodicat
Next
tot /= 10^8
Print tot
Sleep