3

This is really more a question of approach, but I'm presenting it in php.

Suppose we had a list of four percentages that a give event will occur on iteration.

array=('walk the dog'=>.25,'read the paper'=>.25,'drink coffee'=>.0,'listen to music'=>.50)

(Keys are just tests - in practice this is going to be used in a strategy pattern that applies different methods to an object off chance using call_user_func())

In a loop, what would be the best way to choose one of these events by random-adjusted selection taking their respective weights into account (IE, for this particular configuration, there will be a lot of music listened to and not a lot of coffee drank)

Currently I have something like:

for($i=1;$i<=3;$i++){
  $rand = 1/rand(1,10);
  //choose from the list
}

But this would require mapping the percentages to value ranges (50-100% become listening to music, 0-25% walking the dog and 25-50 reading the paper) because the 'value' for that iteration is calculated as a floating point number between 0 and 1. This kind of changes the way the percentages need to be recorded.

Is there any kind of approach that would preserve the identity of the values as the real probability they should be chosen rather than ranges? It would definitely make the code more configurable since I have to make these values adjustable. Please let me know if I've been too vague.

Toto
  • 89,455
  • 62
  • 89
  • 125
DeaconDesperado
  • 9,977
  • 9
  • 47
  • 77

4 Answers4

5

I'd do something like:

$arr = array (
    'walk the dog'    =>  array('min' =>  0, 'max' =>  25),
    'read the paper'  =>  array('min' => 25, 'max' =>  50),
    'drink coffee'    =>  array('min' =>  0, 'max' =>   0),
    'listen to music' =>  array('min' => 50, 'max' => 100),
);

$rnd = rand(1,100);
foreach($arr as $k =>$v) {
    if ($rnd > $v['min'] && $rnd <= $v['max']) {
        echo $k,"\n";
    }
}
Toto
  • 89,455
  • 62
  • 89
  • 125
3

Have you considered using the percentage values as a cumulative number?

$array = array('walk the dog'=>.25,'read the paper'=>.25,'drink coffee'=>.0,'listen to music'=>.50);

$rand = rand();
$cur = 0;
foreach ($key=>$val in $array) {
  $cur += $val;
  if ($rand <= $cur)
    return $key;
}

This assumes the values in the array add up to 1. Ordering doesn't matter either...

CNeo
  • 736
  • 1
  • 6
  • 10
1
$arr = array('walk the dog'=>25,'read the paper'=>25,'listen to music'=>50);
$rand = rand(1,100);
$sum = 0;
$action = '';

foreach ($arr as $k => $v) {
    $sum .= $k;
    if ($sum <= $k)
        $action = $v;
}

Removed those with a key value of zero, and multiplied the key-value with 100 to make it more readable.

OptimusCrime
  • 14,662
  • 13
  • 58
  • 96
0

You can restructure the array in such a way that the events are stored with decreasing probability (for performance) and the value corresponding to an event is the probability of this particular event plus the value of a previous element (which means that the last element will always have the value 1.).

Then you just iterate over the array and return the event for which its value is greater than $rand.

crenate
  • 3,370
  • 2
  • 20
  • 13