Thread View: sci.math.stat
2 messages
2 total messages
Started by Van Bagnol
Thu, 04 Apr 1991 18:37
Any normally-distributed random number functions?
Author: Van Bagnol
Date: Thu, 04 Apr 1991 18:37
Date: Thu, 04 Apr 1991 18:37
19 lines
1308 bytes
1308 bytes
I am looking for a C or Fortran function that would generate a pseudorandom variable but with a *normal* distribution instead of uniform distribution. (I.e., return a random value Z with a mean of 0 and a std. dev. of 1.) * Does anyone know of any algorithm(s)? * What are their pros/cons? So far we have thought of using a "Monte Carlo" approach: take a sample of "n" uniformly random numbers between 0 and 1, then get its mean and s.d. and adjust to taste. Is there a faster and/or more elegant way? (BTW, how big should "n" be, 25? 30?) Many thanks in advance. -- Van Bagnol / Group One, Ltd. / (415) 398-7565 / ...!uunet!group1!van "Be cool, have fun." / "Parang lumakad ko sa loob ang panaginip..."
TP code (Was Re: Any normally-distributed random number functions?)
Author: Ajay Shah
Date: Thu, 04 Apr 1991 22:19
Date: Thu, 04 Apr 1991 22:19
275 lines
9652 bytes
9652 bytes
{$N+} {$E+}
UNIT distrns;
{All kind of code connected with various distributions.}
INTERFACE
{NORMAL DISTRIBUTION}
{3 kernel functions:}
function StdNorm(x:double):double; {standard normal density function}
function CNorm(x:double):double; {cumulative standard normal df}
function BivNormDens(X, Y, rho:double):double; {bivariate normal density}
{oft-used gloss}
function lndnorm(x:double):double; {compute ln(stdnormdens at x), in 3 flops!}
function NormalDensity(mu, sigma, x:double):double;
function NormArea(x1, x2:double):double; {area between x1 and x2 on N(0,1)}
function mi(x:double):double; {ratio of density upon area}
{random numbers:}
function StdRandNorm:double;
function RandNorm(myoo, sigma:double):double;
procedure StdBivNorRand(rho:double; var X1, X2:double);
{returns draw from mean zero, variance 1, corr. coeff rho
bivariate normal distribution.}
{FEW OTHERS}
function CauchyRand(var h, m:double):double;
function ExpRand(var a:double):double;
function Uniform(var a,b:double):double;
IMPLEMENTATION
const
NORMMAX = 20.0; {place where we force normal density to zero,
similar restrictions on normal probability.}
SQRT2 = 1.414213562373095049;
SQRTPI = 1.772453850905516027;
OneUponSqrt2Pi = 0.398942280401433;
TWOPI = 6.2831853071795864769;
LNSQRT2PI = -0.9189385332046727417803296;
{----------------------------------------------------------------------------}
function StdNorm(x:double):double; {standard normal density function}
begin
if abs(x) > NORMMAX then StdNorm := 0.0
else StdNorm := exp(-0.5*x*x)*OneUponSqrt2Pi
end;
{----------------------------------------------------------------------------}
function NormalDensity(mu, sigma, x:double):double;
var tmp:double;
begin
tmp := (x-mu)/sigma;
NormalDensity := StdNorm(tmp)/sigma
end;
{----------------------------------------------------------------------------}
function CNorm(x:double):double;
{Objective :
given x, returns integral from -infinity to x of the std.
normal distribution.
Precision:
all the digits printed in Abramovitch and Stegun (14? 17?)
Method :
it knows 3 approximations, in the regions
(I) abs(x/root2) < 0.46
(II) 0.46 < abs(x/root2) < 4
(III) 4 < abs(x/root2)
in each of these regions, it approximates CNorm by a polynomial-ish.
Speed :
On my 386/387 at 20 MHz, no cache, Nortons SI = 21.1,
region (I): 0.2 milliseconds
region (II): 0.47 milliseconds
region (III): 0.43 milliseconds
History :
* Originally a CACM algorithm, forget which,
* Translated from fortran into C by Luke Tierney (XLispStat),
* Translated from C into Turbo Pascal by Ajay Shah, 14 Dec 1990.
}
const
P10 = 242.66795523053175; P11 = 21.979261618294152;
P12 = 6.9963834886191355; P13 = -0.035609843701815385;
Q10 = 215.05887586986120; Q11 = 91.164905404514901;
Q12 = 15.082797630407787; Q13 = 1.0;
P20 = 300.4592610201616005; P21 = 451.9189537118729422;
P22 = 339.3208167343436870; P23 = 152.9892850469404039;
P24 = 43.16222722205673530; P25 = 7.211758250883093659;
P26 = 0.5641955174789739711; P27 = -0.0000001368648573827167067;
Q20 = 300.4592609569832933; Q21 = 790.9509253278980272;
Q22 = 931.3540948506096211; Q23 = 638.9802644656311665;
Q24 = 277.5854447439876434; Q25 = 77.00015293522947295;
Q26 = 12.78272731962942351; Q27 = 1.0;
P30 = -0.00299610707703542174; P31 = -0.0494730910623250734;
P32 = -0.226956593539686930; P33 = -0.278661308609647788;
P34 = -0.0223192459734184686; Q30 = 0.0106209230528467918;
Q31 = 0.191308926107829841; Q32 = 1.05167510706793207;
Q33 = 1.98733201817135256; Q34 = 1.0;
{These constants are totally specific to this function; hence they're
not at the top of this file with sensible names.}
var
sn:integer;
R1, R2, R3, y, y2, y3, y4, y5, y6, y7, erf, erfc, z, z2, z3, z4 : double;
begin
if (x < -NORMMAX) then
begin
CNorm := 0.0; exit
end;
if (x > NORMMAX) then
begin
CNorm := 1.0; exit
end;
y := x/SQRT2;
if (y < 0.0) then begin
y := -y;
sn := -1;
end
else sn := 1;
y2 := y * y;
y4 := y2 * y2;
y6 := y4 * y2;
if (y < 0.46875) then
begin
R1 := P10 + (P11*y2) + (P12*y4) + (P13*y6);
R2 := Q10 + (Q11*y2) + (Q12*y4) + (Q13*y6);
erf := y * R1 / R2;
if (sn = 1) then CNorm := 0.5 + (erf/2.0)
else CNorm := 0.5 - (erf/2.0);
exit
end;
if (y < 4.0) then
begin
y3 := y2 * y;
y5 := y4 * y;
y7 := y6 * y;
R1 := P20 + (P21*y) + (P22*y2) + (P23*y3) + (P24*y4) +
(P25*y5) + (P26*y6) + (P27*y7);
R2 := Q20 + (Q21*y) + (Q22*y2) + (Q23*y3) + (Q24*y4) +
(Q25*y5) + (Q26*y6) + (Q27*y7);
erfc := exp(-y2) * R1 / R2;
if (sn = 1) then CNorm := 1.0 - (erfc/2.0)
else CNorm := erfc/2.0;
exit
end;
z := y4;
z2 := z * z;
z3 := z2 * z;
z4 := z2 * z2;
R1 := P30 + (P31*z) + (P32*z2) + (P33*z3) + (P34*z4);
R2 := Q30 + (Q31*z) + (Q32*z2) + (Q33*z3) + (Q34*z4);
erfc := (exp(-y2)/y) * ( (1.0/SQRTPI) + (R1/(R2 * y2)) );
if (sn = 1) then CNorm := 1.0 - (erfc/2.0)
else CNorm := erfc/2.0;
end;
{----------------------------------------------------------------------------}
function lndnorm(x:double):double; {compute ln(stdnormdens at x), in 3 flops!}
begin
lndnorm := LNSQRT2PI - (sqr(x)/2.0)
end;
{----------------------------------------------------------------------------}
function NormArea(x1, x2:double):double; {area between x1 and x2 on N(0,1)}
begin {You just get junk if x1 > x2}
NormArea := CNorm(x2) - CNorm(x1)
end;
{----------------------------------------------------------------------------}
function mi(x:double):double; {ratio of density upon area}
begin
mi := StdNorm(x)/cnorm(x)
end;
{----------------------------------------------------------------------------}
function bivnormdens(X, Y, rho:double):double;
{(X,Y) is a point in x-y plane, rho = correlation coefficient.
The function computes bivariate std. normal density at (x,y).}
var
rho2, tmp, denom, power : double;
begin
rho2 := sqr(rho);
tmp := 1.0 - rho2;
denom := TWOPI*sqrt(tmp);
power := (X*(X - (2.0*rho*Y)) + sqr(Y))/tmp;
bivnormdens := exp(-power/2.0) / denom
end;
{----------------------------------------------------------------------------}
function stdrandnorm:double;
{reference: page 953 of Abramowitch and Stegun}
begin
stdrandnorm := sqrt(-2.0*ln(random))*cos(TWOPI*random)
end;
{----------------------------------------------------------------------------}
function randnorm(myoo, sigma:double):double;
begin
randnorm := (sigma*stdrandnorm) + myoo
end;
{----------------------------------------------------------------------------}
function tan(x:double):double;
begin
tan := sin(x)/cos(x)
end;
{----------------------------------------------------------------------------}
function CauchyRand(var h, m:double):double;
begin
CauchyRand := h*(tan(pi*(random - 0.5))) + m
end;
{----------------------------------------------------------------------------}
function ExpRand(var a:double):double;
begin
ExpRand := -ln(random)/a;
end;
{----------------------------------------------------------------------------}
function Uniform(var a,b:double):double;
begin
Uniform := a + (random*(b-a))
end;
{----------------------------------------------------------------------------}
procedure StdBivNorRand(rho:double; var X1, X2:double);
{reference: page 953 of Abramowitch and Stegun}
var U1, U2, tmp1, tmp2 : double;
begin
U1 := random; U2 := random;
{generating uniformly distributed random numbers ain't cheap!}
tmp1 := sqrt(-2.0*ln(U1));
tmp2 := TWOPI*U2;
X1 := tmp1*cos(tmp2);
X2 := tmp1*sin(tmp2);
{X1 and X2 are now independent, std. normal random numbers.
That was cheaper than calling RandNorm twice.}
X2 := (rho*X1) + (X2*sqrt(1.0-(rho*rho)))
{X1 and X2 are now drawn from the required bivariate normal}
end;
begin {unit init}
randomize
end.
--
_______________________________________________________________________________
Ajay Shah, (213)734-3930, ajayshah@usc.edu
The more things change, the more they stay insane.
_______________________________________________________________________________
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads