1
votes

What I'm trying to do isn't exactly a Gaussian distribution, since it has a finite minimum and maximum. The idea is closer to rolling X dice and counting the total.

I currently have the following function:

function bellcurve($min=0,$max=100,$entropy=-1) {
    $sum = 0;
    if( $entropy < 0) $entropy = ($max-$min)/15;
    for($i=0; $i<$entropy; $i++) $sum += rand(0,15);
    return floor($sum/(15*$entropy)*($max-$min)+$min);
}

The idea behind the $entropy variable is to try and roll enough dice to get a more even distribution of fractional results (so that flooring it won't cause problems).

It doesn't need to be a perfect RNG, it's just for a game feature and nothing like gambling or cryptography.

However, I ran a test over 65,536 iterations of bellcurve() with no arguments, and the following graph emerged:
Graph
(source: adamhaskell.net)

As you can see, there are a couple of values that are "offset", and drastically so. While overall it doesn't really affect that much (at worst it's offset by 2, and ignoring that the probability is still more or less where I want it), I'm just wondering where I went wrong.

Any additional advice on this function would be appreciated too.

UPDATE: I fixed the problem above just by using round instead of floor, but I'm still having trouble getting a good function for this. I've tried pretty much every function I can think of, including gaussian, exponential, logistic, and so on, but to no avail. The only method that has worked so far is this approximation of rolling dice, which is almost certainly not what I need...

2
could you change your code above to reflect your new changes? If it creates a bell curve and that is what you want why is 'rolling dice' unsuitable? - VoronoiPotato
The code hasn't changed - I tried several things but none of them did what I wanted. The problem with rolling dice is that it's not accurate, especially if more dice are rolled. I need finer control over the bell's parameters. - Niet the Dark Absol
What do you mean it's not accurate? It's a bell curve. The more dice are rolled the more bell shaped it will get. Gurps for example uses the sum of 3 six sided dice to achieve a bell curve. if you want to weight the bell just add or subtract dice. Example, player is more likely to be successful here, add another dice to their pool, and have them drop the lowest die. - VoronoiPotato
Well, let's say I want damage to be modified by a factor that's most likely to be around 1 but could be from 50% to 150%. A proper funciton would give me a good probability curve for that need, whereas rolling dice... well, how would you roll dice and get a total of 101 different possibilities? - Niet the Dark Absol
two 10 sided dice, or 1 101 sided dice, or 20 5 sided dice. Dice are a wholly suitable source of randomness, hence why they are used in gambling. - VoronoiPotato

2 Answers

1
votes

If you are looking for a bell curve distribution, generate multiple random numbers and add them together. If you are looking for more modifiers, simply multiply them to the end result.

Generate a random bell curve number, with a bonus of 50% - 150%. Sum(rand(0,15), rand(0,15) , rand(0,15))*(rand(2,6)/2)

Though if you're concerned about rand not providing random enough numbers you can use mt_rand which will have a much better distribution (uses mersenne twister)

0
votes

The main issue turned out to be that I was trying to generate a continuous bell curve based on a discrete variable. That's what caused holes and offsets when scaling the result.

The fix I used for this was: +rand(0,1000000)/1000000 - it essentially takes the whole number discrete variable and adds a random fraction to it, more or less making it continuous.

The function is now:

function bellcurve() {
    $sum = 0;
    $entropy = 6;
    for($i=0; $i<$entropy; $i++) $sum += rand(0,15);
    return ($sum+rand(0,1000000)/1000000)/(15*$entropy);
}

It returns a float between 0 and 1 inclusive (although those exact values are extremely unlikely), which can then be scaled and rounded as needed.

Example usage:

$damage *= bellcurve()-0.5; // adjusts $damage by a random amount
                            // between 50% and 150%, weighted in favour of 100%