Generating random results by weight in PHP?

Viewed 36556

I know how to generate a random number in PHP but lets say I want a random number between 1-10 but I want more 3,4,5's then 8,9,10's. How is this possible? I would post what I have tried but honestly, I don't even know where to start.

13 Answers

For an efficient random number skewed consistently towards one end of the scale:

  • Choose a continuous random number between 0..1
  • Raise to a power γ, to bias it. 1 is unweighted, lower gives more of the higher numbers and vice versa
  • Scale to desired range and round to integer

eg. in PHP (untested):

function weightedrand($min, $max, $gamma) {
    $offset= $max-$min+1;
    return floor($min+pow(lcg_value(), $gamma)*$offset);
}
echo(weightedrand(1, 10, 1.5));

There's a pretty good tutorial for you.

Basically:

  1. Sum the weights of all the numbers.
  2. Pick a random number less than that
  3. subtract the weights in order until the result is negative and return that number if it is.

Since I used IainMH's solution, I may as well share my PHP code:

<pre><?php

// Set total number of iterations
$total = 1716;

// Set array of random number
$arr = array(1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5);
$arr2 = array(0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 5);

// Print out random numbers
for ($i=0; $i<$total; $i++){

    // Pick random array index
    $rand = array_rand($arr);
    $rand2 = array_rand($arr2);

    // Print array values
    print $arr[$rand] . "\t" . $arr2[$rand2] . "\r\n";

}

?></pre>

I used this:

mt_rand($min, mt_rand($min, $max));

it give more lower values and less higher values, since the more the value is high the more is cutted out by one of the mt_rand

The probability is linearly increasing in the lower values, forming a square diagonal (see maths lower)

PRO: easy and strightforward

CON: maybe too simple so not enough weightable or balanceable for some use case

Maths:

let i index of i-nth value from min to max,

let P(i) the probability of obtaining the i-nth value,

let N=max-min:

P(i)=(1+N-i)/sum(1,N)

Since N is equals for all terms:

P(i) is proportional to N-i

so, in facts, the probability is linearly increasing in the lower values, forming a square diagonal

Variants:

you can write variants:

mt_rand($min, mt_rand(1, mt_rand(1, $max))); //value more given in low part

mt_rand(mt_rand($min, $max), $max); //mirrored, more upper values than lower

...

i used Brad's answar and changed it a little to fit my situation and add more flexibility

i have an array with array value

$products = [
 ['id'=>1,'name'=> 'product1' , 'chance'=>2] ,
 ['id'=>2,'name'=> 'product2' , 'chance'=>7]
]

first i shuffle the products array

shuffle($products );

then you can pass it to the function

function getRandomWeightedElement(array $products) {

$chancesSum = 0;
foreach ($products as $product){
    $chancesSum += (int) $product['chance'];
}

$rand = mt_rand(1, $chancesSum);
$range = 0;

foreach ($products as $product) {
    $range += (int) $product['chance'];
    $compare = $rand - $range;
    if ($compare <= 0){
        return (int) $product['id'];
    }
}}
Related