This recipe produces random numbers from a normal (bell-shaped) distribution. (The rand() command in Tcl produces uniformly distributed random numbers.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # The following procedure generates two independent normally distributed
# random numbers with mean 0 and vaviance stdDev^2. If you only need 1
# random number, choose either one.
proc randNormal {stdDev} {
global dRandNormal1
global dRandNormal2
set u1 rand()
set u2 rand()
set dRandNormal1 [expr $stdDev * sqrt(-2 * log($u1)) * cos(2 * 3.14159 * $u2)]
set dRandNormal2 [expr $stdDev * sqrt(-2 * log($u1)) * sin(2 * 3.14159 * $u2)]
}
# The following code is only used to demonstrate the procedure and should
# be replaced by your own code.
for {set i 1} {$i<10} {incr i} {
randNormal 1
puts $dRandNormal1
puts $dRandNormal2
}
|
This recipe is useful when you need random numbers from a normal distribution, such as simulating measurement noise. This solution is a very simple, straightforward way to convert Tcl's uniformly distributed random numbers to normally distributed random numbers. If you want to use this for Monte Carlo simulation you should pre-process rand() as described at http://mini.net/tcl/1551
I am not sure why the commands
are there. Semantically they have no effect. The code in the recipe is equivalent to
Code Explanation. u1 and u2 need to be the same in both calculations, so I chose them up front. If you don't keep them the same, there is no guarantee that the normal random numbers will be independent.
Bill Kappele (bill@mathoptions.com)
Code Correction. Thank you, Andreas. The code should read
set u1 [expr rand()] set u2 [expr rand()]
to function correctly.
Need to be the same. Need a third variable to make both the same
set temp [expr rand()]
set u1 $temp
set u2 $temp
Two variables. I believe two variables are needed. I am not familiar with the current language you are using, but the variables in the formulas must be uncorrelated to provide gaussian like distribution. i.e.:
a = rand_0_1() ;
b = rand_0_1() ;
y1 = sigma * sqrt(-2.0 * a )*cos(2.0 * pi * b ) ;
y2 = sigma * sqrt(-2.0 * a )*sin(2.0 * pi * b ) ;
the random numbers y1 and y2 are now orthogonal and normally distributed. I think.
Thanks for the recipe. I needed to doublecheck my code.
Setting the two variables beforehand isn't needed to keep the resulting random numbers independent. When you think about it, andreas' numbers can't be dependent since there's no variables in his two expressions and each rand() result is indepedent of another. What it does is that it saves two rand() calls. That probably doesn't matter until you change rand() to something that takes entropy from your system, like reading from /dev/random, and you waste half of it.
If TCL's rand() returned a number in the range [0,1) (including 0 but not 1), which I've always thought, u1 or u2 would eventually become 0 and crash at the log() part. log(0) = infinity. The Box-Muller tranform expects a range of (0,1] (not including 0 but 1) and you could get that with 1 - rand(). I just read that the range is (0,1) (not including either 0 or 1) so it probably doesn't matter, but it's a safe thing to do in case it changes. I'm pretty sure rand() will never return 1.
Use acos(-1) for pi instead of typing digits. That way, when computers become 128-bit, 256-bit etc, your pi precision will follow.
This returns just one number, without wasting anything. rand() can be replaced with another function of your liking that returns a uniformly distibuted random number greater than or equal to 0 and less than 1.