I wonder if it is possible in python to produce random numbers
according to a user defined distribution?
Unfortunately the random module does not contain the distribution I
need :-(
Sure there's a way but it won't be very efficient. Starting with an
arbitrary probability density function over some range, you can run it
through a quadrature routine to create a cumulative density function
over that range. Use random.random() to create a uniform variate x.
Then use a bisecting search to find x in the cumulative density
function over the given range.
from __future__ import division
from random import random
def integrate(f, lo, hi, steps=1000):
dx = (hi - lo) / steps
lo += dx / 2
return sum(f(i*dx + lo) * dx for i in range(steps))
def make_cdf(f, lo, hi, steps=1000):
total_area = integrate(f, lo, hi, steps)
def cdf(x):
assert lo <= x <= hi
return integrate(f, lo, x, steps) / total_area
return cdf
def bisect(target, f, lo, hi, n=20):
'Find x between lo and hi where f(x)=target'
for i in range(n):
mid = (hi + lo) / 2.0
if target < f(mid):
hi = mid
else:
lo = mid
return (hi + lo) / 2.0
def make_user_distribution(f, lo, hi, steps=1000, n=20):
cdf = make_cdf(f, lo, hi, steps)
return lambda: bisect(random(), cdf, lo, hi, n)
if __name__ == '__main__':
def linear(x):
return 3 * x - 6
lo, hi = 2, 10
r = make_user_distribution(linear, lo, hi)
for i in range(20):
print r()