Ben
Ben

Reputation: 1638

PHP: Shuffle array, so no value is like the correspondent one

There is an array with names, for example:

$donalds_nephews = array('Huey', 'Dewey', 'Louie');

array
(
    [0] => Huey
    [1] => Dewey
    [2] => Louie
)

I want to shuffle this array, but ensure that no value of the original array has the same key as the shuffled one.

$donalds_nephews_shuffled = shuffle($donalds_nephews);

This could result in 6 possible permutations:

  1. Huey, Dewey, Louie
  2. Huey, Louie, Dewey
  3. Dewey, Louie, Huey
  4. Dewey, Huey, Louie
  5. Louie, Dewey, Huey
  6. Louie, Huey, Dewey

1st, 2nd, 4th and 5th must not be the result.

What's the best way to do so? It's for Secret Santa.

Upvotes: 2

Views: 351

Answers (5)

A.F.
A.F.

Reputation: 41

It is an old question, but you asked for the best way, so how about this?

function santaYates($array) {
    $returnArray = array_values($array); // Cause we need a clean numeric array
    $secure = false;
    for($i = count($returnArray) - 1; $i > 0; $i--) {
        $r = mt_rand(0, $i-1); //subtract 1 from $i to force a new place.
        $tmp = $returnArray[$i];
        $returnArray[$i] = $returnArray[$r];
        $returnArray[$r] = $tmp;
    }

    return $returnArray;
}

It works very similar to the Fisher-Yates shuffle.

There is just one little difference: We permit to use the same key, so every entry will get a new place (cause we substract 1 from $i when we do the randomize step).

Working Demo

Upvotes: 0

Gianpaolo Di Nino
Gianpaolo Di Nino

Reputation: 1147

just cause i need this for my secret santa :)

<?php

    function compareArrays($original, $shuffled){   
        for($i = 0; $i < count($original); $i++ ){
            if($original[$i] == $shuffled[$i]){
                return false;
            }
        }
        return true;
    }

    $donalds_nephews = array('Huey', 'Dewey', 'Louie','Igor','Stephan');

    //avoid loops
    for($j = 0; $j < 50; $j++){
        $shuffled = $donalds_nephews;
        shuffle($shuffled);
        $good = compareArrays($donalds_nephews, $shuffled);

        if($good) break;
    }

    if($good){
        echo "here we go!\n";
        foreach($shuffled as $k => $v){
            echo "$k => $v \n";
        }
    }
    else { 
        echo "try again \n";
    }

?>

Upvotes: 1

symcbean
symcbean

Reputation: 48357

It's for Secret Santa.

Then start with a better algorithm. Currently you seem to be inferring that the key is a present giver and the value a present receiver (or vice versa). This then entails the additional check (and possible re-iteration of the shuffling) to ensure that nobody ends up giving a present to themselves.

But what if you just consider it as an ordered list of names, such that each entry gives to the next person on the list:

$victims=array('Huey', 'Dewey', 'Louie');
shuffle($victims);
$giver='';
foreach($victims as $receiver) {
  if ($giver) print "$giver gives to $receiver\n";
  $giver=$receiver;
}
$receiver=array_shift($victims);
print "$giver gives to $receiver\n";

Upvotes: 1

Andy Lester
Andy Lester

Reputation: 93636

Don't make this complicated by trying to put all of this into one function.

Here's your pseudocode:

$givers  = array( 'Huey', 'Dewey', 'Louie' );
$getters = $givers;

foreach ( $givers as $giver ) {
    do {
        pick a random $getter from $getters;
    } until $getter <> $giver;
    delete $getter from $getters;
    print "$giver gives to $getter\n";
}

Upvotes: 0

deceze
deceze

Reputation: 522032

Shuffle the original array, then make a copy of it and shift all entries one over, then match the two back together to get your matches.

Upvotes: 5

Related Questions