11

Javascript's Math.random() returns a psuedo-random number with "uniform" distribution.

I need to generate a random number in the range [0,1] that is skewed to either side. (Meaning, higher chance of getting more numbers next to 0 or next to 1)

Ideally I would like to have a parameter to set this curve.

I supposed I can do Math.random^2 to get such a result, but what more sophisticated ways are there to achieve this?

OpherV
  • 6,787
  • 6
  • 36
  • 55
  • Per below answer, Poisson distributions aren't [0,1]. They're [0,infinity). So which do you want? – djechlin Apr 19 '13 at 18:30
  • +1, this is a good question with non-trivial solution. – Nishanth Apr 20 '13 at 12:27
  • @djechlin technically Poisson random variables have support over {0,…,∞}, this matters for thinking about taking samples from Poisson Processes over spaces that generate a Poisson random variable based on the underlying rate distribution on the [0,∞) space that is usually considered in Poisson Processes. If you consider the entirety of that space, you get a Poisson random variable number of events with mean ∞, which traditionally is viewed as a valid edge case where you expect the Poisson random variable = ∞ (w.p.=1). – mpacer Sep 25 '15 at 00:39

4 Answers4

17

I think you want beta distribution with alpha=beta=0.5

It is possible to transform uniform random number to beta distribution using inverse cumulative distribution.

unif = Math.random()

I am not familiar with javascript, but this should be clear:

beta = sin(unif*pi/2)^2

PS: you can generate many such numbers and plot histogram enter image description here

Edit:

For skewing towards 0, transform the beta values as -

beta_left = (beta < 0.5) ? 2*beta : 2*(1-beta);

enter image description here

For skewing towards 1, transform as -

beta_right = (beta > 0.5) ? 2*beta-1 : 2*(1-beta)-1;

enter image description here

Nishanth
  • 6,932
  • 5
  • 26
  • 38
  • Interesting! I will attempt this. Thanks for the graph illustration. What would I do if I wish to have the distribution skew to one side only? – OpherV Apr 20 '13 at 16:25
  • 1
    Well there are ways of mapping uniform distribution to *any* distribution using inverse CDF method. I will updated my answer to transform the already numbers into right/left skew. – Nishanth Apr 20 '13 at 16:51
4

I just came up with a simpler way to get random numbers skewed to each side, and which doesn't have any dependencies.

This method uses two of Javascript's regular random numbers. The first is multiplied with itself (the larger the exponent, the bigger the skewing effect), and the second chooses which side of the distribution to skew to.

function skewedRandom() {
    const a = Math.pow(Math.random(), 2);
    if (Math.random() < 0.5) {
        return a;
    }
    return 1 - a;
}

In this code the exponent is set to 2. A sample histogram from 10,000 executions with the exponent at 2 as above:

Histogram with exponent 2

With exponent 3:

Histogram with exponent 3

And with exponent 10: Histogram with exponent 10

tremby
  • 9,541
  • 4
  • 55
  • 74
  • 1
    I note a possible issue with this method. Math.random() produces numbers from 0 inclusive to 1 exclusive. This method, however, can produce numbers from 0 to 1 inclusive. If that's an issue for you, a possible fix might be to change the last line to `return 1 - Number.EPSILON - a;`. Though I'm not sure if this could ever produce a value slightly less than 0. – tremby Jan 10 '18 at 21:53
3

I think you need to rethink your question. The Poisson is a counting distribution specified in terms of a rate, such as how many occurrences of something do I see on average per time period. It yields positive integers so the result can't be just in the range [0,1]. Can you please clarify what you want?

Regardless, to generate a Poisson with rate lambda one algorithm is:

threshold = Math.exp(-lambda)
count = 0
product = 1.0
while (product *= rand) >= threshold {
      count += 1
}
return count

where "rand" is the function call for a Uniform(0,1). I don't know javascript, but that should be straightforward enough for you to implement.

Responding to edited question:

There are several distributions that generate outcomes on a bounded range, but many of them aren't for the faint of heart, such as the Johnson family or the Beta distribution.

An easy one would be triangle distributions. Sqrt(rand) will give a triangle distribution bunched towards 1, while (1-Sqrt(1-rand)) will give a triangle distribution bunched towards zero.

A more general triangle with the mode (most frequent value) at m (where 0 <= m <= 1) can be generated with

if rand <= m
    return m * Sqrt(rand)
else
    return 1 - ((1 - m) * Sqrt(1 - rand))

Note that each invocation of rand is a separate Uniform random number, this won't be correct if you generate one value for rand and use it throughout.

pjs
  • 18,696
  • 4
  • 27
  • 56
  • You're absolutely right. I was thinking one thinking about this all backwards :) Thanks for setting me straight (and in the right direction!). In any case, good to know regarding the algorithm – OpherV Apr 19 '13 at 18:48
2

Yoy can use window.crypto.getRandomValues where available

<div id="result"></div>

var randVal = new Uint8Array(1);

window.crypto.getRandomValues(randVal);

document.getElementById("result").textContent = randVal[0] / 255;

On jsfiddle

(if that's what your asking for, I'm not sure)

Or maybe as this

<div id="result"></div>

function poissonRandomNumber(lambda) {
    var L = Math.exp(-lambda),
        k = 0,
        p = 1;

    do {
        k = k + 1;
        p = p * Math.random();
    } while (p > L);

    return k - 1;
}

document.getElementById("result").textContent = poissonRandomNumber(100);

also on jsfiddle

Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • 2
    `crypto.getRandomValues()` is good for when you need numbers that are tamper-proof or cryptographically secure. `Math.random()` is fine everywhere else, and is better when speed is necessary, such as with games (but not for authentication). – Solomon Ucko May 05 '19 at 00:52