0

I have an array containing the following data:

(
    [Blue] => 15.3
    [Red] => 64.7
    [Green] => 20.0
)

The probability of Blue being picked is 15.3%, Red has a high chance of being picked with 64.7%, and Green has a 20% chance.

I need to create a function that will pick one of these colours at random, but also factoring in probability.

mmmbaileys
  • 1,233
  • 4
  • 18
  • 33
  • hint: if you generate a random integer between 0 and 1000 (which might be done with the common `rand()` function and the modulus operator), you can trivially say some values map to Blue, others to Red etc. – Tony Delroy May 19 '14 at 10:55
  • For a general solution take a look at http://stackoverflow.com/questions/13635448/generate-random-numbers-within-a-range-with-different-probabilities/13637027#13637027. – user515430 May 19 '14 at 17:57

2 Answers2

3

I am pretty sure that this function should do the trick for you, you can adjust it for points after the decimal if needed:

Edit: Did a touch of actual testing and modified the code ever so slightly (as well as adding loads of output for you to see what is happening:

<?php 
function chance($input=array())
{
    echo 'The Max Value can be: '.(array_sum($input)*10).'<br>';
    $number=rand(0,array_sum($input)*10);
    echo 'Checking for: '.$number.'<br>';
    $starter=0;
    foreach($input as $key => $val)
    {
        $starter+=$val*10;
        echo 'Current value being tested against is: '.$starter.' which is '.$key.'<br>';
        if($number<=$starter)
        {
            $ret=$key;
            break;
        }

    }

    return $ret;
}

$array=array('Blue' => 15.3, 'Red' => 64.7, 'Green' => 20.0);

for($i=0;$i<10;$i++)
{
    echo chance($array).'<br><br>';
}
?>

Example Output:

The Max Value can be: 1000
Checking for: 355
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Red

The Max Value can be: 1000
Checking for: 63
Current value being tested against is: 153 which is Blue
Blue

The Max Value can be: 1000
Checking for: 692
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Red

The Max Value can be: 1000
Checking for: 803
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Current value being tested against is: 1000 which is Green
Green

The Max Value can be: 1000
Checking for: 360
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Red

The Max Value can be: 1000
Checking for: 174
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Red

The Max Value can be: 1000
Checking for: 117
Current value being tested against is: 153 which is Blue
Blue

The Max Value can be: 1000
Checking for: 769
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Red

The Max Value can be: 1000
Checking for: 462
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Red

The Max Value can be: 1000
Checking for: 418
Current value being tested against is: 153 which is Blue
Current value being tested against is: 800 which is Red
Red
Fluffeh
  • 33,228
  • 16
  • 67
  • 80
  • What if the array is not sorted by value? – Andresch Serj May 19 '14 at 11:00
  • @AndreschSerj Doesn't make a difference, the random is between 0 and the sum - once the `$number` is greater than or equal to the value, the key will be returned. The bigger the value in a key, the larger the chances that it will be the value that the random number falls into? – Fluffeh May 19 '14 at 11:02
  • Ah, my Mistake. Now i see it. Nice! – Andresch Serj May 19 '14 at 11:09
  • 1
    @mmmbaileys No worries, I like to lurk around and find problems to solve like this one, then try to find an elegant solution to them. Much more interesting than coming across yet another question where someone forgot a `"` in their code... – Fluffeh May 19 '14 at 11:43
2

Using mt_rand you can pick a min and a max. So you use 0 to 100 for instance.

$randomValue = (mt_rand ( 0 , 1000 ) / 10)

Then, just pick the breach appropriate to your percentage like this:

if($randomValue <= 15.3) { $pick = 'Blue';}
elseif($randomValue <= 64.7) { $pick = 'Red';}
else { $pick = 'Green';}

Using your array you could easily to this in a function. Just sort your array by value and then handle appropriately.

function getRandomColor($colorsWithProbability) {
  sort($colorsWithProbability);
  $last = '';

  foreach($colorsWithProbability as $color => $probability) {
    $last = $color;
    if($randval <= $probability) {
      return $color;
    }
  }
  return $last;
}
Andresch Serj
  • 35,217
  • 15
  • 59
  • 101
  • He wants the probability with one decimal. Consider doing something like this instead: `$randomValue = mt_rand(0,1000) / 10;` – Viktor Svensson May 19 '14 at 10:58
  • @ViktorSvensson The OP did not state that it should be one decimal nor did the OP state a gender ;-p – Andresch Serj May 19 '14 at 11:09
  • 1
    Well, since the probability is expressed with a decimal (15.3 for example). With the above code, it will not matter if the probability is 15.0 or 15.1, it will still be equally probable to get "picked" (the decimal part won't matter). Regarding the gender you're right though, I have no idea if the OP is male or female! :-) – Viktor Svensson May 19 '14 at 11:13
  • She-male ;-D thanks for the contribution guys – mmmbaileys May 19 '14 at 11:44
  • @ViktorSvensson You are right about that decimal issue! I edited my answer. And now we alos know the gender as well. The day is saved :D – Andresch Serj May 19 '14 at 11:55