How to generate normal distributed random numbers in Prolog?

In general, languages provide you with a uniform distribution over 0 to 1. There are various algorithms for getting from that uniform distribution to another distribution, but this case is particularly common so there are a few ways to do it.

If you need a modest amount of random values in a normal distribution, the Box-Muller transform is a very simple algorithm, it amounts to a little math on a few uniform random values:

random_normal(N) :-
    random(U1), random(U2),
    Z0 is sqrt(-2 * log(U1)) * cos(2*pi*U2),
    Z1 is sqrt(-2 * log(U1)) * sin(2*pi*U2),
    (N = Z0 ; N = Z1).

This algorithm consumes two uniform values and produces two normal values. I'm providing both solutions. Other ways of doing this might be better for some applications. For instance, you could use asserta/1 and retract/1 to cache the second value and use it without computing, though messing around in the dynamic store may be about as bad as doing the other work (you'd have to benchmark it). Here's the use:

?- random_normal(Z).
Z = -1.2418135230345024 ;
Z = -1.1135242997982466.

?- random_normal(Z).
Z = 0.6266801862581797 ;
Z = -0.4934840828548163.

?- random_normal(Z).
Z = 0.5525713772053663 ;
Z = -0.7118660644436128.

I'm not greatly confident of this but it may get you over the hump.


If you are using SWI-Prolog or SWISH then another option would be to use embedded R, which gives you a lot of flexibility with stats and probabilities.

http://swish.swi-prolog.org/example/Rserve.swinb

The R project provides statistical computing and data vizualization. SWISH can access R through Rserve.

Integrative statistics with R:

Real is a c-based interface for connecting R to Prolog. See the documentation at doc/html/real.html for more information. There is also a paper [1] and a user's guide in doc/guide.pdf.

Real works on current versions of SWI and YAP. As of version 1.1 there is support for using Real on SWI web-servers.