Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

How do I get random numbers that follow standard distribution?

( #26889=categorized question: print w/ replies, xml ) Need Help??
Contributed by sinan on Aug 09, 2000 at 00:33 UTC
Q&A  > math


Description:

I need to generate random numbers; but they have to follow the standard distribution. The usual random number generation routines do not do this.

In other words, if I have a range of 1 to 100, I want the numbers around 50 to occur frequently, and the numbers about 1 or about 100 to occur with a very low frequency. With the usual random number generation routines, all numbers have the same frequency.

There must be a simple algorithm that create standard distribution numbers using the usual generator. (i.e. An algorithm that converts such numbers)
Does anybody know any function that does this? Can anybody write a simple algorithm for this purpose?

Please do not tell me any non-standard modules because I have trouble installing them.

TIA,
Sinan

Answer: How do I get random numbers that follow standard distribution?
contributed by tilly

If you don't need a perfect normal distribution then you can get a very good approximation by choosing several random numbers and adding them together. You can then multiply then add by appropriate constants to get the desired average and variance.

For most purposes, adding a dozen is good enough.

If you want better than that, try installing Math::Random.

DISCLAIMER: I am one dissertation short of a PhD in math. (It is hand-written, needed an income so I never cleaned it up and typed it.) Therefore for all intents and purposes, I am a mathematician. :-)

Answer: How do I get random numbers that follow standard distribution?
contributed by lhoward

How about something like this:

sub f{ my $t=0; $t+=rand() for(1..5); return int($t*20+1); }
You can vary the number of iterations (1..5) and scaling factor (20) based on the exact distribution curve you want.

There are several other ways to approach this problem. If efficiency were of paramount importance, I would try using statistical method to derive a function that would aproximate the curve of how in frequency you would map numbers from 0 to 0.9999 (the range of perl's rand function) into the range you want (1 to 100).

Answer: How do I get random numbers that follow standard distribution?
contributed by grackle

The question posted asks for a non-module solution, and an excellent one was provided by lhoward and tilly. Here is a solution using a CPAN module.

I looked for Math::Random on CPAN and couldn't find it. There are modules for random numbers, such as Crypt::TrulyRandom, but I believe they will only give you better uniformly distributed random variables (correct me if I'm wrong). For a normal distribution, you can use Math::CDF (Cumulative Distribution Functions). The relevant function is Math::CDF::qnorm(), which will give you what you want when fed a random number x, 0<x<1, with a uniform distribution (i.e., what you get from rand()). (It supposedly returns a value for the inputs x=1 and x=0, but I don't know what those values could be, you'll have to try it).

So the function you want is:

$myrand = qnorm(rand());

(or replace rand() with your function of choice).

One caveat: I don't know how the Math::CDF module calculates these values. If it provides more precision than you need, it could be a big waste of processing time. In that case lhoward and tilly's solution, summing several random variables, works fine. It would be nice to have bounds for the errors, but I can't help you there. Calculate, experiment, or find a reference.... Perhaps this would be a good place to post the results.

Answer: How do I get random numbers that follow standard distribution?
contributed by Anonymous Monk

sub norm{ my $v1,$v2,$r; if( defined $v2 ){ $v1 = $v2; undef $v2; }else{ do{ $v1=rand(2)-1; $v2=rand(2)-1; $r = $v1*$v1+$v2*$v2; }while( $r >= 1 || $r == 0 ); $r = sqrt(-2*(log $r)/$r); $v1 *= $r; $v2 *= $r; } return $v1; }
Answer: How do I get random numbers that follow standard distribution?
contributed by Roy Johnson

The Perl Cookbook provides this code:

sub gaussian_rand { my ($u1, $u2); # uniformly distributed random numbers my $w; # variance, then a weight my ($g1, $g2); # gaussian-distributed numbers do { $u1 = 2 * rand() - 1; $u2 = 2 * rand() - 1; $w = $u1*$u1 + $u2*$u2; } while ( $w >= 1 ); $w = sqrt( (-2 * log($w)) / $w ); $g2 = $u1 * $w; $g1 = $u2 * $w; # return both if wanted, else just one return wantarray ? ($g1, $g2) : $g1; }
and notes:
The gaussian_rand function [generates] two numbers with a mean of 0 and a standard deviation of 1 (i.e., a Gaussian distribution). To generate numbers with a different mean and standard deviation, multiply the output of gaussian_rand by the new standard deviation, and then add the new mean
Answer: How do I get random numbers that follow standard distribution?
contributed by jlistf

i think you're going to have to use statistics. i don't believe Perl has anything built-in to do it, though there might be a module on CPAN. Perl's rand($n) will give you numbers (more or less) uniformly distributed between 0 and 1. its up to you to convert this to a normalized distribution.

Answer: How do I get random numbers that follow standard distribution?
contributed by sinan

I finally did something that solves the problem partially.

The two following functions do that. The first function, rands will return a "standard" randomized integer. It has two input values: The first input variables denote the range of the output. (i.e. 0 to input) The second one will denote the precision: Higher it is, closer the result will be to the normal distribution. 5 is more than enough for many purposes.

The second function (random) is a little bit more tricky. The first input is the range, just like in the first function. The second input is a string which can either be "low" or "high". The third input is a limit. If the second input is "low", then the third input denotes the lowest possible number. If it is high, then it denotes the highest possible number. All the results that are outside either the lowest or the highest frequency will be ignored and "re-rolled".

Here is the snippet:

sub rands{ my $t=0; $t = 0; $t+= ( rand()* ($_[0]/$_[1]) ) for(1..$_[1]); return int($t+1); } sub random{ my ($no,$type,$limit) = @_; $t = rands($no,5); if($type eq "low"){ while ($t<$limit){ $t = rands($no,5) } } elsif($type eq "high"){ while ($t>$limit){ $t = rands($no,5) } } }
I was not able to "skew" my results, so I used this _harsh_ method of cutting off instead.

One final note: You can set the second input variable of rands to a low number (2,3) to get a more homogenous distribution, i.e. a distribution with "high variance".

Sinan
Answer: How do I get random numbers that follow standard distribution?
contributed by chas

By "standard distribution", I think you mean "standard Normal distribution". One way to get a random sample from such a distribution is as follows: Get a random sample x_1,...x_n from a uniform distribution on the unit interval. (this is easily done using the usual random number generators you refer to.) Then for each x_i, compute z_i satisfying
x_i=(1/sqrt(2 pi))\int_{-\infty}^z_i e^{-t^2/2}dt. (I.e. x_i=F(z_i) where F is the distribution function for a N(0,1).) Then z_1,...z_n will be a random sample from a N(0,1). You have to write a bit of code to solve for z_i, of course. I've done similar things in c, but not perl so I can't display perl code to achieve this.
The previous suggestion to add the results of several uniformly distributed random numbers is interesting; one needs independence to make use of the Central Limit Theorem, so I'm not sure exactly how to set that up, but it should be possible.
The method I described first is pretty standard.
chas

Please (register and) log in if you wish to add an answer



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others musing on the Monastery: (11)
    As of 2014-07-14 12:36 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      When choosing user names for websites, I prefer to use:








      Results (259 votes), past polls