s c h e m a t i c s : c o o k b o o k

/ WebHome / TOC / NumberChapter / Cookbook.NumberRecipeBiasedRands

This Web


WebHome 
WebChanges 
TOC (with recipes)
NewRecipe 
WebTopicList 
WebStatistics 

Other Webs


Chicken
Cookbook
Erlang
Know
Main
Plugins
Sandbox
Scm
TWiki  

Schematics


Schematics Home
Sourceforge Page
SchemeWiki.org
Original Cookbook
RSS

Scheme Links


Schemers.org
Scheme FAQ
R5RS
SRFIs
Scheme Cross Reference
PLT Scheme SISC
Scheme48 SCM
MIT Scheme scsh
JScheme Kawa
Chicken Guile
Bigloo Tiny
Gambit LispMe
GaucheChez

Lambda the Ultimate
TWiki.org

Generating Biased Random Numbers

Problem

Instead of producing a series of numbers distributed uniformly over an interval, we need data following one of the classical distributions such as the normal distribution (i.e. the numbers should give a "bell curve").

Solution

The library solution

The easy solution is use the "random.plt" library from PLaneT.

Let's try generating numbers with a Gaussian distribution (a normal distribution with mean 0 and standard deviance 1):

> (require (planet "random.ss" ("schematics" "random.plt" 1 0)))
> (random-gaussian)
0.7386912134436788
> (random-gaussian)
-0.4388994504610697

If we want to mimick a stocastic variable, we can use random-source-make-gaussians and a source of random bits.

> (define X (random-source-make-gaussians default-random-source))
> (X)
0.5826066449247809
> (X)
0.7865269446783535

Alternatively, one could simple define X as

> (define X (lambda () (random-gaussian)))

The do it your self solution

Given a distribution, lookup an algorithm in a statistics reference. If you can't find an algorithm, consult a numerical analyst.

Let's examine the case of the normal distribution. The two parameters mu (mean) and sigma (standard deviance) determines a specific normal distribution.

; derived from example in the documentation of SRFI27
(require (lib "27.ss" "srfi"))

(define (make-normal-distributed-variable mu sigma)
  (let ((mu    (* 1.0 mu))
        (sigma (* 1.0 sigma))
        (next  #f))
    (lambda ()
      (cond
        (next  (let ((result next))
                 (set! next #f)
                 (+ mu (* sigma result))))
        (else  (let loop ()
                 (let* ((v1 (- (* 2.0 (random-real)) 1.0))
                        (v2 (- (* 2.0 (random-real)) 1.0))
                        (s  (+ (* v1 v1) (* v2 v2))))
                   (cond
                     ((>= s 1.0) (loop))
                     (else       (let ((scale (sqrt (/ (* -2.0 (log s)) s))))
                                   (set! next (* scale v2))
                                   (+ mu (* sigma scale v1))))))))))))

An example of usage:

> (define X (make-normal-distributed-variable 0 1))
> (X)
0.7386912134436788
> (X)
-0.4388994504610697
> (X)
0.5826066449247809

If you are unsatisfied with the fact that you get the exact same numbers as above, then randomize the source of random numbers:

> (random-source-randomize! default-random-source)

Discussion

The algorithm used is the polar Box Muller method. The algorithm takes two independent uniformly distributed random numbers between 0 and 1 (represented in the code as (random-real)) and generates two numbers with a mean of my and standard deviation sigma. The method produces two numbers at a time, so since we only need one, the second is saved for later in the variable next.


Comments about this recipe

Note that the Perl Cookbook includes an interesting discussion of converting a set of values (and weights) into a distribution. This should also be converted to Scheme and shown here.

Mathematically-inclined Schemers should also take a good look at random.ss, which contains these and many other statistical methods.

It's also worth noting that if a bell-curve type thing is all you're looking for, generating two or more random numbers and taking the average will tend to favor the middle values. For example, consider a pair of dice: there is exactly one combination out of 36 that yields 2 and one that yields 12 (the outlying values), while there are six combinations that yield 7 (the center value). You could also use a weighed average to reduce the effect if averaging two random numbers produces a bell curve which is too steep for your application.

Contributors

-- BrentAFulgham - 14 May 2004

-- JensAxelSoegaard - 01 Jun 2004 -- JensAxelSoegaard - 12 Dec 2006

[TODO: Move the following remarks to another recipe]

If you wish to randomly select from a set of weights and values, convert the weights into a probability distribution, then use the resulting distribution to pick a value.

If you have a list of weights and values you want to randomly pick from, follow this two-step process: First, turn the weights into a probability distribution with weight_to_dist below, and then use the distribution to randomly pick a value with weighted_rand:

[TODO: Use the random-source-make-discretes from random.ss to solve the above problem]

CookbookForm
TopicType: Recipe
ParentTopic: NumberRecipes
TopicOrder: 100

 
 
Copyright © 2004 by the contributing authors. All material on the Schematics Cookbook web site is the property of the contributing authors.
The copyright for certain compilations of material taken from this website is held by the SchematicsEditorsGroup - see ContributorAgreement & LGPL.
Other than such compilations, this material can be redistributed and/or modified under the terms of the GNU Lesser General Public License (LGPL), version 2.1, as published by the Free Software Foundation.
Ideas, requests, problems regarding Schematics Cookbook? Send feedback.
/ You are Main.guest