Reputation:
I need a function that randomizes an array similar to what shuffle does, with the difference that each element has different chances.
For example, consider the following array:
$animals = array('elephant', 'dog', 'cat', 'mouse');
elephant has an higher chance of getting on the first index than dog. Dog has an higher chance than cat and so on. For example, in this particular example elephant could have a chance of 40% in getting in the 1st position, 30% of getting on the 2nd position, 20% on getting on 3rd and 10% getting on last.
So, after the shuffling, the first elements in the original array will be more likely (but not for sure) to be in the first positions and the last ones in the last positions.
Upvotes: 2
Views: 482
Reputation: 14304
Normal shuffle may be implemented just as
We can adjust dropping step, drop every element not into whole range, but at some sliding window. Let N
would be amount of elements in array, window width would be w
and we'll move it at each step by off
. Then off*(N-1) + w
would be total width of the range.
Here's a function, which distorts elements' positions, but not completely at random.
function weak_shuffle($a, $strength) {
$len = count($a);
if ($len <= 1) return $a;
$out = array();
$M = mt_getrandmax();
$w = round($M / ($strength + 1)); // width of the sliding window
$off = ($M - $w) / ($len - 1); // offset of that window for each step.
for ($i = 0; $i < $len; $i++) {
do {
$idx = intval($off * $i + mt_rand(0, $w));
} while(array_key_exists($idx, $out));
$out[$idx] = $a[$i];
}
ksort($out);
return array_values($out);
}
$strength = 0
~normal shuffle.$strength = 0.25
~your desired result (40.5%, 25.5%, 22%, 12% for elephant
)$strength = 1
first item will never be after last one.$strength >= 3
array is actually never shuffledPlayground for testing:
$animals = array( 'elephant', 'dog', 'cat', 'mouse' );
$pos = array(0,0,0,0);
for ($iter = 0; $iter < 100000; $iter++) {
$shuffled = weak_shuffle($animals, 0.25);
$idx = array_search('elephant', $shuffled);
$pos[$idx]++;
}
print_r($pos);
Upvotes: 4
Reputation: 76434
You have an array, let's say of n elements. The probability that the i'th element will go to the j'th position is P(i, j). If I understood well, the following formula holds:
(P(i1, j1) >= P(i2, j2)) <=> (|i1 - j1| <= |j1 - i1|)
Thus, you have a Galois connection between the distance in your array and the shuffle probability. You can use this Galois connection to implement your exact formula if you have one. If you don't have a formula, you can invent one, which will meet the criteria specified above. Good luck.
Upvotes: 1
Reputation: 1952
Try to use this algorithm:
$animals = [ 'elephant', 'dog', 'cat', 'mouse' ]; // you can add more animals here
$shuffled = [];
$count = count($animals);
foreach($animals as $chance => $animal) {
$priority = ceil(($count - $chance) * 100 / $count);
$shuffled = array_merge($shuffled, array_fill(0, $priority, $animal));
}
shuffle($shuffled);
$animals = array_unique($shuffled);
Upvotes: 2