Generate random number with a non-uniform distribu

2019-02-06 14:14发布

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?

4条回答
ら.Afraid
2楼-- · 2019-02-06 14:24

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

查看更多
啃猪蹄的小仙女
3楼-- · 2019-02-06 14:40

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

查看更多
甜甜的少女心
4楼-- · 2019-02-06 14:41

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

查看更多
等我变得足够好
5楼-- · 2019-02-06 14:45

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.

查看更多
登录 后发表回答