4

I have an array as so:

$animals = array (
    'giraffe',
    'lion',
    'hippo',
    'dog',
    'cat',
    'rabbit',
    'fly',
    'hamster',
    'gerbil'
    'goldfish'
);

That is the order i want the array to be in apart from these 2 - hamster and gerbil

I would like to randomise between which one comes first. I know i can use:

shuffle($animals);

To randomise them all but i want to do it for just these 2. So if i was to do a print_r($animals) i may get hamster come before gerbil but another time get gerbil before hamster

odd_duck
  • 3,941
  • 7
  • 43
  • 85
  • 2
    note their position, remove them, shuffle, put them back in – Tymoteusz Paul Nov 03 '14 at 16:46
  • Should those two values be randomized in the entire array, or just in terms of their own positions? – BenM Nov 03 '14 at 16:47
  • 1
    So do you want to swap `hamster` and `gerbil` 50% of the time, or do you want to randomize their position in the array? – Halcyon Nov 03 '14 at 16:47
  • I want to randomise their positions instead of a straight 50/50 split, and i want to randomise them in terms of their own position rather than in the entire array – odd_duck Nov 03 '14 at 16:49
  • @odd_duck: So in this case, you'd like to shuffle the 7th and 8th indices of the array? – Amal Murali Nov 03 '14 at 16:51

4 Answers4

9

You can splice the array for those to elements, randomize their order and put them back in the original array:

$sub = array_splice($animals, 7, 2);
shuffle($sub);
array_splice($animals, 7, 0, $sub);
var_dump($animals);
N3R4ZZuRR0
  • 2,400
  • 4
  • 18
  • 32
AlexL
  • 1,699
  • 12
  • 20
  • 1
    @BenM this is an answer giving the idea on how solve the question. It's still a valid answer even though it didn't involved the actual code part since the asker has the proper knowledge on how to implement the solution. – AlexL Nov 03 '14 at 16:51
  • @BenM I have to agree with Alex... This IS an answer for the user's issue ;) – Ares Draguna Nov 03 '14 at 16:58
  • 3
    I completely agree with Alex here, just because it doesn't give out a copy/paste solution, doesn't make it any less of an answer. I actually prefer that, as it still requires for the user to do some work. – Tymoteusz Paul Nov 03 '14 at 17:14
  • OK, I don't understand where my original comment went, but when I commented, there was literally nothing more here than the opening sentence. Anyway, this now is a valid answer :) – BenM Nov 03 '14 at 20:36
  • +1, any idea on the efficiency of this over other approaches? eg shuffle only (needed) indexes and then just permute/re-arrange the original array? – Nikos M. Feb 13 '15 at 00:05
  • @NikosM. It shuffles only the two items and then put them back in the original array. So i'd say it's pretty efficient unless you want to reinvent the wheel. – AlexL Feb 13 '15 at 22:16
  • @AlexLinte, correct, but splicing an array, can be an expensive operation, i have given an [alternative answer](http://stackoverflow.com/a/28491007/3591273) which does not require splice, it is a variation on fisher-yates-knuth shuffle (simnilar to php's shuffle function) – Nikos M. Feb 14 '15 at 12:41
1

Adding 2 variations of Fisher-Yates-Knuth unbiased shuffling algorithm, with only included indices or excluded indices (php-like pseudo-code)

function shuffle_include( $a, $inc ) 
{
    // $a is array to shuffle
    // $inc is array of indices to be included only in the shuffle
    // all other elements/indices will remain unaltered

    // fisher-yates-knuth shuffle variation O(n)
    $N = count($inc);
    while ( $N-- )
    { 
        $perm = rnd( 0, $N ); 
        $swap = $a[ $inc[$N] ]; 
        $a[ $inc[$N] ] = a[ $inc[$perm] ]; 
        $a[ $inc[$perm] ] = $swap; 
    }
    // in-place
    return $a;
}

function shuffle_exclude( $a, $exc ) 
{
    // $a is array to shuffle
    // $exc is array of indices to be excluded from the shuffle
    // all other elements/indices will be shuffled
    // assumed excluded indices are given in ascending order
    $inc = array();
    $i=0; $j=0; $l = count($a); $le = count($exc)
    while ($i < $l)
    {
        if ($j >= $le || $i<$exc[$j]) $inc[] = $i;
        else $j++;
        $i++;
    }
    // rest is same as shuffle_include function above

    // fisher-yates-knuth shuffle variation O(n)
    $N = count($inc);
    while ( $N-- )
    { 
        $perm = rnd( 0, $N ); 
        $swap = $a[ $inc[$N] ]; 
        $a[ $inc[$N] ] = $a[ $inc[$perm] ]; 
        $a[ $inc[$perm] ] = $swap; 
    }
    // in-place
    return $a;
}

Example:

$a = array(1,2,3,4,5,6);

print_r( shuffle_include( $a, array(0,1,2) ) );
// sample output: [2,1,3,4,5,6] , only 0,1,2 indices are shuffled

print_r( shuffle_exclude( $a, array(0,1,2) ) );
// sample output: [1,2,3,6,5,4], all other indices are shuffled except 0,1,2

NOTE That PHP's shuffle function itself uses a variation of Fisher-Yates-Knuth shuffling algorithm

NOTE2 All shuffle algorithms given (and PHP's original shuffle function) have a (average) time complexity of $O(n)$ (n=size of array to shuffle)

for other variations of shuffle see:

  1. Efficiently pick n random elements from PHP array (without shuffle)
Community
  • 1
  • 1
Nikos M.
  • 8,033
  • 4
  • 36
  • 43
0
$animals = array (
'giraffe',
'lion',
'hippo',
'dog',
'cat',
'rabbit',
'fly',
'goldfish'
);
$other = array('hamster','gerbil');
$allAnimals = array();
foreach($animals as $key => $animal){
    if($key == 7){
        $allAnimals = array_merge($allAnimals,shuffle($other));
    }
$allAnimals[] = $animal;
}
mohe14
  • 47
  • 6
0
function shuffle_include($a, $include_indexes)    
{    
    $b = array();    
    foreach ($include_indexes as $i => $v)    
        $b[] = $a[$v];    
            
    shuffle($b);    
    
    foreach ($include_indexes as $i => $v)    
        $a[$v] = $b[$i];    
     
    return $a;    
}

Example:

$animals = array (
'giraffe',
'lion',
'hippo',
'dog',
'cat',
'rabbit',
'fly',
'hamster',
'gerbil',
'goldfish');

$new_animals = shuffle_include($animals, array(7,8));