Randomize a PHP array with a seed?

Viewed 21422

I'm looking for a function that I can pass an array and a seed to in PHP and get back a "randomized" array. If I passed the same array and same seed again, I would get the same output.

I've tried this code

//sample array
$test = array(1,2,3,4,5,6);
//show the array
print_r($test);

//seed the random number generator
mt_srand('123');
//generate a random number based on that
echo mt_rand();
echo "\n";

//shuffle the array
shuffle($test);

//show the results
print_r($test);

But it does not seem to work. Any thoughts on the best way to do this?

This question dances around the issue but it's old and nobody has provided an actual answer on how to do it: Can i randomize an array by providing a seed and get the same order? - "Yes" - but how?

Update

The answers so far work with PHP 5.1 and 5.3, but not 5.2. Just so happens the machine I want to run this on is using 5.2.

Can anyone give an example without using mt_rand? It is "broken" in php 5.2 because it will not give the same sequence of random numbers based off the same seed. See the php mt_rand page and the bug tracker to learn about this issue.

12 Answers

A variant that also works with PHP version 7.2, because the php function create_function is deprecated in the newest php version.

mt_srand($seed);

$getMTRand = function () {
    return mt_rand();
};

$order = array_map($getMTRand, range(1, count($array)));
array_multisort($order, $array);
return $array;

I guess this will do the job :

    function choose_X_random_items($original_array , $number_of_items_wanted = -1 , $seed = FALSE ){

//save the keys
foreach ($original_array as $key => $value) {

    $original_array[$key]['key_memory'] = $key;

}

$original_array = array_values($original_array);
$results = array();
if($seed !== FALSE){srand($seed);}
$main_random = rand();
$random = substr($main_random,0,( $number_of_items_wanted == -1 ? count($original_array) : min($number_of_items_wanted,count($original_array)) ));
$random = str_split($random);

foreach ($random AS $id => $value){


    $pick = ($value*$main_random) % count($original_array);
    $smaller_array[] = $original_array[$pick];
    unset($original_array[$pick]);
        $original_array = array_values($original_array);

}


//retrieve the keys
foreach ($smaller_array as $key => $value) {

    $smaller_array[$value['key_memory']] = $value;
    unset($smaller_array[$value['key_memory']]['key_memory']);
    unset($smaller_array[$key]);

}

return $smaller_array;

}

In order to not limit the resulting array, set $number_of_items_wanted to -1 In order to not use a seed, set $seed to FALSE

Seeded shuffle while maintaining the key index:

function seeded_shuffle(array &$items, $seed = false) {
    
    mt_srand($seed ? $seed : time());
    
    $keys = array_keys($items);
    $items = array_values($items);
    
    for ($i = count($items) - 1; $i > 0; $i--) {
        $j = mt_rand(0, $i);
        list($items[$i], $items[$j]) = array($items[$j], $items[$i]);
        list($keys[$i], $keys[$j]) = array($keys[$j], $keys[$i]);
    }
    
    $items = array_combine($keys, $items);
}

A simple solution:

$pool = [1, 2, 3, 4, 5, 6];
$seed = 'foo';

$randomIndex = crc32($seed) % count($pool);
$randomElement = $pool[$randomIndex];

It might not be quite as random as the Fisher Yates shuffle, but I found it gave me more than enough entropy for what I needed it for.

Based on @Gumbo, @Spudley, @AndreyP answers, it works like that:

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

srand(123); //srand(124);
$order = array_map(function($val) {return rand();}, range(1, count($arr)));
array_multisort($order, $arr);

var_dump($arr);

Home Made function, using crc32(note:also returns negative value(https://www.php.net/manual/en/function.crc32.php))

$sortArrFromSeed = function($arr, $seed){
    $arrLen = count($arr);
    $newArr = [];
    
    $hash = crc32($seed); // returns hash (0-9 numbers)
    $hash = strval($hash);
    while(strlen($hash) < $arrLen){
    $hash .= $hash;
    }
    
    for ($i=0; $i<$arrLen; $i++) {
        $index = (int) $hash[$i] * (count($arr)/9); // because 0-9 range  
        $index = (int) $index; // remove decimal
        if($index !== 0) $index--;
    
        array_push($newArr, $arr[$index]);
        unset($arr[$index]);
        $arr = array_values($arr);
    }
    return $newArr;
};


// TESTING
$arr = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
$arr = $sortArrFromSeed($arr,'myseed123');
echo '<pre>'.print_r($arr,true).'</pre>';
Related