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
Based on @Allain's answer/link, I worked up this quick function in PHP. You will have to modify it if you want to use non-integer weighting.
/**
* getRandomWeightedElement()
* Utility function for getting random values with weighting.
* Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50)
* An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%.
* The return value is the array key, A, B, or C in this case. Note that the values assigned
* do not have to be percentages. The values are simply relative to each other. If one value
* weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66%
* chance of being selected. Also note that weights should be integers.
*
* @param array $weightedValues
*/
function getRandomWeightedElement(array $weightedValues) {
$rand = mt_rand(1, (int) array_sum($weightedValues));
foreach ($weightedValues as $key => $value) {
$rand -= $value;
if ($rand <= 0) {
return $key;
}
}
}
-
5Do the `$weightedValues` have to be in ascending order for this to work? – chiborg Nov 25 '15 at 21:29
-
6@chiborg I had the same initial gut feeling, but it turns out that the array does not have to be sorted as all. I verified it empirically on shuffled weighted arrays, by calling the function many times and verifying that the numbers statistically matched. Whatever the order, they do; the results are consistent. However, the order greatly influences the execution speed: biggest weights first yield the maximum speed, while lowest weights first yield the worst performance. – BenMorel Mar 31 '16 at 23:50
-
I don't get how this is supposed to work given your annotation about relativity. If you look here: https://3v4l.org/0uSJu you see that random numbers that land on exact value return a key; but thereafter there is no value to land on. – user1718888 Feb 19 '17 at 17:33
-
1Here is a similar situation in case this is helpful for someone to understand what's going on here: https://stackoverflow.com/a/12571681/470749 – Ryan Jan 20 '20 at 17:59
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));

- 528,062
- 107
- 651
- 834
-
1I know this question is old, but what does `$gamma` do in this snippet? – OptimusCrime May 05 '12 at 21:25
-
3@Optimus: it's a weighting factor: the output of the function is its input to the power of gamma, where the input is between 0 and 1. So for example for gamma=0.5 you get a square root curve, which curves upwards from 0 quicker than the straight line, so you get higher numbers. See eg [wiki](http://en.wikipedia.org/wiki/Gamma_correction) for info on gamma curves (traditionally applied for image correction purposes) – bobince May 05 '12 at 21:41
-
@bobince : Ah, brilliant! Would 0.218 give a curve like the picture in the wiki? I think that is more like the bias I am looking for. – OptimusCrime May 06 '12 at 14:42
-
1@Optimus: 0.218 is a very steep gamma; the one on the wiki page is 1/2.2 (0.455). – bobince May 06 '12 at 18:30
-
1I thinks, the gamma range was `gamma > 0`. In this case, If the gamma was `=0` then return `3` for always! that is not in `1-2` range. And so, If you want number luck is always `$max`, the gamma must be like `0.000001` and for luck is always `$min`, the gamma must be like `999999`. and `1` is for normal act. see examples: http://phpio.net/s/5xxb – Nabi K.A.Z. Nov 21 '17 at 14:41
-
The floor function gives $max+1 value if lcg_value() returns 1. The return row should look like this: return floor($min + pow(lcg_value(), $gamma) * $offset - 0.0000001); It solves the "gamma == 0" problem, too. – Nagy Zoltán May 09 '21 at 21:29
There's a pretty good tutorial for you.
Basically:
- Sum the weights of all the numbers.
- Pick a random number less than that
- subtract the weights in order until the result is negative and return that number if it is.

- 91,574
- 70
- 187
- 238
-
Also, this does not have the memory overhead of the prior answer (build another array with the desired distribution and selected randomly from it) – Crescent Fresh Jan 15 '09 at 00:55
This tutorial walks you through it, in PHP, with multiple cut and paste solutions. Note that this routine is slightly modified from what you'll find on that page, as a result of the comment below.
A function taken from the post:
/**
* weighted_random_simple()
* Pick a random item based on weights.
*
* @param array $values Array of elements to choose from
* @param array $weights An array of weights. Weight must be a positive number.
* @return mixed Selected element.
*/
function weighted_random_simple($values, $weights){
$count = count($values);
$i = 0;
$n = 0;
$num = mt_rand(1, array_sum($weights));
while($i < $count){
$n += $weights[$i];
if($n >= $num){
break;
}
$i++;
}
return $values[$i];
}

- 66,836
- 64
- 257
- 336
-
1This answer and the tutorial that it was copied from are flawed because the `mt_rand()` minimum should not be `0`, it should be `1`. This means the weight on the first element will be favored more than intended. [http://sandbox.onlinephpfunctions.com/code/454ecce15a148b0934164afb8297076640bf228b](Demo of issue) Please edit your answer and remove the dodgy tutorial's hyperlink. – mickmackusa Apr 23 '17 at 12:42
/**
* @param array $weightedValues
* @return string
*/
function getRandomWeightedElement(array $weightedValues)
{
$array = array();
foreach ($weightedValues as $key => $weight) {
$array = array_merge(array_fill(0, $weight, $key), $array);
}
return $array[array_rand($array)];
}
getRandomWeightedElement(array('A'=>10, 'B'=>90));
This is very easy method. How get random weighted element. I fill array variable $key. I get $key to array $weight x. After that, use array_rand to array. And I have random value ;).

- 630
- 7
- 5
Plain and fair. Just copy/paste and test it.
/**
* Return weighted probability
* @param (array) prob=>item
* @return key
*/
function weightedRand($stream) {
$pos = mt_rand(1,array_sum(array_keys($stream)));
$em = 0;
foreach ($stream as $k => $v) {
$em += $k;
if ($em >= $pos)
return $v;
}
}
$item['30'] = 'I have more chances than everybody :]';
$item['10'] = 'I have good chances';
$item['1'] = 'I\'m difficult to appear...';
for ($i = 1; $i <= 10; $i++) {
echo weightedRand($item).'<br />';
}
Edit: Added missing bracket at the end.

- 1
- 1

- 169
- 7
You can use weightedChoice from Non-standard PHP library. It accepts a list of pairs (item, weight) to have the possibility to work with items that can't be array keys. You can use pairs function to convert array(item => weight)
to the needed format.
use function \nspl\a\pairs;
use function \nspl\rnd\weightedChoice;
$weights = pairs(array(
1 => 10,
2 => 15,
3 => 15,
4 => 15,
5 => 15,
6 => 10,
7 => 5,
8 => 5,
9 => 5,
10 => 5
));
$number = weightedChoice($weights);
In this example, 2-5 will appear 3 times more often than 7-10.

- 4,689
- 1
- 26
- 25
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'];
}
}}

- 93
- 1
- 7
I just released a class to perform weighted sorting easily.
It's based on the same algorithm mentioned in Brad's and Allain's answers, and is optimized for speed, unit-tested for uniform distribution, and supports elements of any PHP type.
Using it is simple. Instantiate it:
$picker = new Brick\Random\RandomPicker();
Then add elements as an array of weighted values (only if your elements are strings or integers):
$picker->addElements([
'foo' => 25,
'bar' => 50,
'baz' => 100
]);
Or use individual calls to addElement()
. This method supports any kind of PHP values as elements (strings, numbers, objects, ...), as opposed to the array approach:
$picker->addElement($object1, $weight1);
$picker->addElement($object2, $weight2);
Then get a random element:
$element = $picker->getRandomElement();
The probability of getting one of the elements depends on its associated weight. The only restriction is that weights must be integers.
Many of the answers on this page seem to use array bloating, excessive iteration, a library, or a hard-to-read process. Of course, everyone thinks their own baby is the cutest, but I honestly think my approach is lean, simple and easy to read/modify...
Per the OP, I will create an array of values (declared as keys) from 1 to 10, with 3, 4, and 5 having double the weight of the other values (declared as values).
$values_and_weights=array(
1=>1,
2=>1,
3=>2,
4=>2,
5=>2,
6=>1,
7=>1,
8=>1,
9=>1,
10=>1
);
If you are only going to make one random selection and/or your array is relatively small* (do your own benchmarking to be sure), this is probably your best bet:
$pick=mt_rand(1,array_sum($values_and_weights));
$x=0;
foreach($values_and_weights as $val=>$wgt){
if(($x+=$wgt)>=$pick){
echo "$val";
break;
}
}
This approach involves no array modification and probably won't need to iterate the entire array (but may).
On the other hand, if you are going to make more than one random selection on the array and/or your array is sufficiently large* (do your own benchmarking to be sure), restructuring the array may be better.
The cost in memory for generating a new array will be increasingly justified as:
- array size increases and
- number of random selections increases.
The new array requires the replacement of "weight" with a "limit" for each value by adding the previous element's weight to the current element's weight.
Then flip the array so that the limits are the array keys and the values are the array values. The logic is: the selected value will have the lowest limit that is >= $pick.
// Declare new array using array_walk one-liner:
array_walk($values_and_weights,function($v,$k)use(&$limits_and_values,&$x){$limits_and_values[$x+=$v]=$k;});
//Alternative declaration method - 4-liner, foreach() loop:
/*$x=0;
foreach($values_and_weights as $val=>$wgt){
$limits_and_values[$x+=$wgt]=$val;
}*/
var_export($limits_and_values);
Creates this array:
array (
1 => 1,
2 => 2,
4 => 3,
6 => 4,
8 => 5,
9 => 6,
10 => 7,
11 => 8,
12 => 9,
13 => 10,
)
Now to generate the random $pick
and select the value:
// $x (from walk/loop) is the same as writing: end($limits_and_values); $x=key($limits_and_values);
$pick=mt_rand(1,$x); // pull random integer between 1 and highest limit/key
while(!isset($limits_and_values[$pick])){++$pick;} // smallest possible loop to find key
echo $limits_and_values[$pick]; // this is your random (weighted) value
This approach is brilliant because isset()
is very fast and the maximum number of isset()
calls in the while loop can only be as many as the largest weight (not to be confused with limit) in the array. For this case, maximum iterations = 2!
THIS APPROACH NEVER NEEDS TO ITERATE THE ENTIRE ARRAY

- 43,625
- 12
- 83
- 136
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
...

- 11,714
- 1
- 86
- 77
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>

- 1,224
- 1
- 14
- 35
function getBucketFromWeights($values) { $total = $currentTotal = $bucket = 0;
foreach ($values as $amount) {
$total += $amount;
}
$rand = mt_rand(0, $total-1);
foreach ($values as $amount) {
$currentTotal += $amount;
if ($rand => $currentTotal) {
$bucket++;
}
else {
break;
}
}
return $bucket;
}
I ugh modified this from an answer here Picking random element by user defined weights
After I wrote this I saw someone else had an even more elegant answer. He he he he.