3

In trying to solve this question, I wrote the following in an attempt to implement the Box-Muller transform to generate random normal variables in pure VBA:

Function RandNorm(Optional mean As Double = 0, Optional sd As Double = 1) As Double
    Dim s As Double
    s = Sqr(-2 * Log(Rnd())) * Cos(6.283185307 * Rnd()) '6.28 etc. is 2*pi
    RandNorm = mean + sd * s
End Function

The following somewhat weak test always works, returning a number close to 0:

Sub test1()
    Randomize
    Dim s As Double
    Dim i As Long
    For i = 1 To 17000000
        s = s + RandNorm()
    Next i
    Debug.Print s / 17000000 
End Sub

On the other hand, the following test never works (because it tries to take the log of 0, which is undefined):

Sub test2()
    Randomize
    Dim s As Double
    Dim i As Long
    Debug.Print Rnd() 'just to clock it
    For i = 1 To 17000000
        s = s + RandNorm()
    Next i
    Debug.Print s / 17000000   
End Sub

The problem is that rnd() returns 0 on average once out of every 2^24 (a bit less than 17,000,000) calls. It is of course easy enough to tweak the definition of RandNorm to avoid the zero (see the linked-to question), but I am still puzzled by the above code. It would make perfect sense to me if each test failed half the time (when the zero is fed into Log()) and worked half the time (when the zero is fed into Cos()). It seems that Randomize avoids at least half of the possible seeds.

Why does Randomize behave this way? Is there a way to seed the random number generator so that all possible states of the random number generator can occur?

On Edit

If I define the following sub:

Sub ReRandomize()
    Dim r As Double
    Randomize
    If Rnd() > 0.5 Then r = Rnd()
End Sub

And modify test1 and test2 above to use ReRandomize instead of Randomize, both of the test subs will fail 50% of the time, so that might answer the part of the question about if there is "a way to seed the random number generator so that all possible states of the random number generator can occur?" It is still mysterious as to why Randomize behaves the way that it does. This is the second time that an Excel VBA question made me realize that Randomize is a weird sub. None of this matters very much for typical use of rnd(), but it does underscore that it is a somewhat low quality random number generator which shouldn't be used for serious statistical work.

John Coleman
  • 51,337
  • 7
  • 54
  • 119
  • Interesting to note is that zero -never- seems to be the first value returned by random after Randomize is called. In addition, as you've noted, the zero value only appears where #ofTimeRndCallAfterRandomize MOD 2 = 0. If you call Randomize every time before calling Rnd() you don't generate any 0's. By calling Rnd() in test2 you force this bad behavior to ONLY generate the zero to be supplied into log as its always the second iteration of rnd being called, whereas in the first test the cos always gets the zero side state. – Mike Jan 28 '19 at 16:40
  • It seems to depend on which seed we use for Rnd() in Debug.Print line, but I don't know why – Rafał B. Jan 28 '19 at 17:32
  • Not sure if this is what you are asking for, but try moving the call to `Randomize` *after* `Debug.Print Rnd()` in `Test2`. This should complete ok (at least it is for me!), I think something changes with the seed when you call `Rnd()` after `Randomize` – Ryan Wildry Jan 29 '19 at 00:34

1 Answers1

0

I simply modified the Rnd calc to not include 0 or 1. You have to remember that the Rnd Function can produce a number (of type double) in the range of 0 or 1. Therefore, it's chances of having a duplicate number are pretty low.

    dbl1stRnd = Rnd()
    dblRnd = (0.9999 - 0.0001) * dbl1stRnd + 0.0001

    s = Sqr(-2 * Log(dblRnd)) * Cos(6.283185307 * dblRnd) '6.28 etc. is 2*pi

Some example outputs of the regular Rnd() function with Randomize:

 3.633606E-02 
 0.2324036 
 0.3460443 
 0.5870923 
 5.553758E-02 
 0.2629338 
 0.2400494 
 0.1982901 
 0.5923058 
 0.7915452 
 0.4874671 
 0.2062811 
 0.5676001 
 0.1178594 
 1.932621E-03 
 0.4326598 
 0.8291379 

I hope this explains some and is what you are looking for.

IAmNerd2000
  • 761
  • 1
  • 4
  • 12
  • The question isn't how to avoid the `0` (which is easy enough to guard against) so much as what is going on with `Randomize`. If fully half (or even more) of the possible states of the random number generator can't arise directly after a call to `Randomize` (as seems to be the case) then using `Randomize` might be causing a subtle bias to creep into the calculations. – John Coleman Jan 28 '19 at 18:55
  • I realize you have no problem with the `0`. However, what I was saying is that randomize generates a number (of Double) from 0 to 1. Therefore, it will not produce duplicates (in all likelihood). I posted some example outputs of Rnd. – IAmNerd2000 Jan 28 '19 at 19:03