🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

2 total messages Started by Van Bagnol Thu, 04 Apr 1991 18:37
Any normally-distributed random number functions?
#651
Author: Van Bagnol
Date: Thu, 04 Apr 1991 18:37
19 lines
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?)
#652
Author: Ajay Shah
Date: Thu, 04 Apr 1991 22:19
275 lines
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