user784637
user784637

Reputation: 16142

How to write a function that returns a subset of elements based on the probability % stored in the element?

I have the following array

$arr = array(
    "person1" => 10,
    "person2" => 10,
    "person3" => 20,
    "person4" => 25,
    "person5" => 35,                    
);

I would like to write a function that takes $arr as an argument and returns 3 elements of the array based on the values stored in each element.

For example if the returned subset yields

$newArr = array(
    "person5" => 35,  
    "person1" => 10,
    "person4" => 25,                  
);

There was a 35% chance that person5 would be the first element stored in $newArr based on the value stored in $arr['person5'] divided by the sum of values stored in the remaining elements. $arr['person5']/($arr['person5'] + $arr['person4'] + $arr['person3'] + $arr['person2'] + $arr['person1'])

There was a ~15% chance that person1 would be the second element stored in $newArr based on the value stored in $arr['person1'] divided by the sum of values stored in the remaining elements. $arr['person1']/($arr['person4'] + $arr['person3'] + $arr['person2'] + $arr['person1'])

There was a ~45% chance that person4 would be the second element stored in $newArr based on the value stored in $arr['person4'] divided by the sum of values stored in the remaining elements. $arr['person4']/($arr['person4'] + $arr['person3'] + $arr['person2'])

How could I write a function that does this?

Upvotes: 1

Views: 88

Answers (1)

Polity
Polity

Reputation: 15130

You're looking for a roulette wheel selection algorithm, see: http://en.wikipedia.org/wiki/Fitness_proportionate_selection

$count = 3;
$arr = array(
    "person1" => 10,
    "person2" => 10,
    "person3" => 20,
    "person4" => 25,
    "person5" => 35,                    
);

$result = array();

// sort from low to high
asort($arr);

// loop 3 times (based on count)
while ($count > 0){

    // get the sum of all persons
    $sum = 0;
    foreach ($arr as $rank){
        $sum += $rank;
    }

    // get a random value between 0 and sum
    $delta = rand(0, $sum);
    $current = 0;

    // keep looping over each item, increasing rank untill $sum has surpassed delta
    // see each item as as person containing a portion of the slice. The bigger the value, the greater the change of being selected
    $current = 0;
    foreach ($arr as $name => $rank){
        $current += $rank;

        if ($delta <= $current){
            $result[$name] = $rank;
            break;
        }
    }

    unset($arr[$name]);

    $count--;
}

Upvotes: 1

Related Questions