I hope someone can help me with this one. I want to take an ordered php array and randomly 'jiggle' it about a bit to change the order but retain some of the original overall structure.
Imagine you have a tray of coloured sequins that make up a picture of a house. If you jiggle the tray a bit then the sequins will move about, but, depending on how much you jiggle it, you will still retain some of that original structure of a house - bit it will be fuzzier. That's what I want to do with a php array.
Let me give an example. Suppose I have the following array:
$Array=Array(
1=>15,
2=>14,
3=>13,
4=>12,
5=>11,
6=>10,
7=>9,
8=>8,
9=>7,
10=>6,
11=>5,
12=>4,
13=>3,
14=>2,
15=>1);
I want to be able to jiggle it about a bit to give something like:
$Array=Array(
1=>13,
2=>15,
3=>12,
4=>14,
5=>11,
6=>8,
7=>7,
8=>10,
9=>5,
10=>6,
11=>9,
12=>4,
13=>2,
14=>1,
15=>3);
The order has been partially randomized but the general downward trend from 15 to 1 remains. I hope this makes sense.
Unless I'm mistaken I don't think there's a native function in php that does this. But does anyone know how this could be achieved?
Instead of using functions like shuffle()
or array_shuffle()
which are optimized to get a result as much shuffeled as possible you should write your own algorithm:
Have a try with a 'bubbling' strategy:
This should preserve the rough position of elements much better than strict randomization, since elements can only move one step during each iteration. So the general trend should be preserved. How much depends on the number of iterations you perform.
Here is a (very simple) example implementation:
#!/usr/bin/php
<?php
// the input array, just as you specified it
$input=array(
1=>15,
2=>14,
3=>13,
4=>12,
5=>11,
6=>10,
7=>9,
8=>8,
9=>7,
10=>6,
11=>5,
12=>4,
13=>3,
14=>2,
15=>1
);
// the algorithm itself, a 'bubbling' function
function array_bubble (&$collection, $limit) {
for ($i=1; $i<=$limit; $i++) {
$pos=rand(min(1,sizeof($collection)-1);
$help=$collection[$pos];
$collection[$pos] =$collection[$pos+1];
$collection[$pos+1]=$help;
}
return $collection;
} // function array_bubble
// here the algorithm is called and the result printed
// note that the '20' in there is the number of iterations. Try changing it!
print_r(array_bubble($input,20));
?>
That script produces an output like this:
Array
(
[1] => 11
[2] => 15
[3] => 13
[4] => 8
[5] => 14
[6] => 12
[7] => 9
[8] => 10
[9] => 5
[10] => 6
[11] => 7
[12] => 4
[13] => 1
[14] => 3
[15] => 2
)
An alternative to the bubbling strategy mentioned by arkascha, you can iterate through the array, and generate a random number with gaussian/normal distribution to swap the current element with. Perhaps better described in code (untested):
function swap (&$arr, $a, $b) {
$tmp=$arr[$a];
$arr[$a]=$arr[$b];
$arr[$b]=$tmp;
}
for ($i = 0; $i < count($arr); $i++) {
$diff = round(stats_rand_gen_normal(0, 3));
$j = max(0, min(count($arr), $i + $diff));
swap($arr, $i, $j);
}
This should only require one pass; the mean and standard deviation of the jiggling should be roughly the parameters to stats_rand_gen_normal.