Did someone know how to generate random numbers with a non-uniform density?
-
Depends... What density function do you need? – Goran Jovic Feb 05 '11 at 18:42
-
You will need to define your required distribution. Otherwise it would be easy to just say something like "Yes, take a good random number, and reset the 4th bit" – Dave Feb 05 '11 at 18:43
-
I need to generate number with a pre-defined function set at runtime (for example 1/sqrt(2*pi) * e^(-x^2) if i want a normal distribution) – BlackShadow Feb 05 '11 at 18:45
-
Generally you are better off working with the inverse CDF than the density. The inverse CDF of a normal is ugly; take a look at the Box-Muller method answer if you need normals. – Paul Apr 04 '14 at 09:14
-
http://en.wikipedia.org/wiki/Inverse_transform_sampling http://en.wikipedia.org/wiki/Rejection_sampling – joriki Feb 05 '11 at 18:43
5 Answers
Easiest solution if applicable: Use C++11 random facilities or the ones from Boost, which have lots of non-uniform distributions for you.
-
+1. Even if the precise distribution is missing, they provide a framework in which you can add your own distribution. – MSalters Feb 07 '11 at 10:53
Use a uniform density RNG, and pass its result through a mapping function to convert to your desired density distribution.

- 70,107
- 14
- 90
- 153
-
1Can you give me an example with a c-like programming language? (just for better understand what you mean for mapping function) – BlackShadow Feb 05 '11 at 18:52
-
1The required mapping function is the inverse cumulative distribution function. The relationship to the density function involves some calculus or numerical integration and then inverting. – Paul Apr 04 '14 at 09:13
You should state what distribution you need. Basically, you use the inverse of the probability function you want. For example, the most common way to get normal distribution is Box-Muller transform.
Here is the code for Box-Muller just to get the idea:
float box_muller(float m, float s) /* normal random variate generator */
{ /* mean m, standard deviation s */
float x1, x2, w, y1;
static float y2;
static int use_last = 0;
if (use_last) /* use value from previous call */
{
y1 = y2;
use_last = 0;
}
else
{
do {
x1 = 2.0 * ranf() - 1.0;
x2 = 2.0 * ranf() - 1.0;
w = x1 * x1 + x2 * x2;
} while ( w >= 1.0 );
w = sqrt( (-2.0 * log( w ) ) / w );
y1 = x1 * w;
y2 = x2 * w;
use_last = 1;
}
return( m + y1 * s );
}

- 8,162
- 3
- 37
- 61
This class takes a distribution as a matrix (each row is a couple of a number and its frequency) and generates random numbers. So you can have Look at main method and run.
public class RandomGenerator {
HashMap<Integer,Range> mappa = new HashMap<Integer,Range>();
Random random = new Random();
int max;
public static void main(String as[]){
int[][] matrice = new int[3][2];
//number 5 occurs 5 times
matrice[0][0] = 5 ;
matrice[0][1] = 5 ;
//number 18 occurs 18 times
matrice[1][0] = 18 ;
matrice[1][1] = 18 ;
//number 77 occurs 77 times
matrice[2][0] = 77 ;
matrice[2][1] = 77 ;
RandomGenerator randomGenerator = new RandomGenerator(matrice);
for (int i = 0; i < 100; i++) {
System.out.println( randomGenerator.getNext() );
}
}
public int getNext(){
int percentile = random.nextInt(max);
Range r =mappa.get(percentile);
return r.getValMax();
}
public HashMap<Integer, Range> getMappa() {
return mappa;
}
public void setMappa(HashMap<Integer, Range> mappa) {
this.mappa = mappa;
}
public RandomGenerator(int[][] distribuzioneOriginale ){
ArrayList<Range> listaRange = new ArrayList<Range>();
int previous = 0;
int totaleOccorrenze = 0;
for (int riga = 0; riga < distribuzioneOriginale.length; riga++) {
Range r = new Range();
r.setValMin(previous);
r.setValMax(distribuzioneOriginale[riga][0]);
r.setOccorrenze(distribuzioneOriginale[riga][1]);
totaleOccorrenze += distribuzioneOriginale[riga][1];
previous = distribuzioneOriginale[riga][0];
listaRange.add(r);
}
int indice = 0;
for (int iRange = 0; iRange < listaRange.size(); iRange++) {
Range r = listaRange.get(iRange);
int perc = (int) ( 1000* (r.getOccorrenze() / (double) totaleOccorrenze) ) ;
for (int i = 0; i < perc; i++) {
mappa.put( i + indice , r);
}
indice += perc;
}
max = indice;
}
class Range{
int valMin;
int valMax;
int occorrenze;
public int getValMin() {
return valMin;
}
public void setValMin(int valMin) {
this.valMin = valMin;
}
public int getValMax() {
return valMax;
}
public void setValMax(int valMax) {
this.valMax = valMax;
}
public int getOccorrenze() {
return occorrenze;
}
public void setOccorrenze(int occorrenze) {
this.occorrenze = occorrenze;
}
}
}
In your comment in the question:
1/sqrt(2*pi) * e^(-x^2)
The only variable is x. x itself will have a uniform density. So just pick a good random number, then stick it into that equation.

- 3,438
- 20
- 13
-
Incidentally, by "good" I mean "uniform density", so quite why I end up with -1, when the same answer gets +5 escapes me! Shouldn't complain though, I got a lucky accepted answer the other day. :) – Dave Feb 13 '11 at 00:24
-
Didn't downvote, but bad suggestion. The suggestion of letting x be uniform random in [0,1] will not provide a normal distribution when x is put through that function. One needs the inverse cumulative distribution function, and the function given by OP is the density function. – Paul Apr 04 '14 at 09:10