使用种子随机化PHP数组?

时间:2011-07-02 15:15:54

标签: php arrays random seed

我正在寻找一个函数,我可以在PHP中传递数组和种子并返回一个“随机”数组。如果我再次传递相同的数组和相同的种子,我会得到相同的输出。

我试过这段代码

//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);

但它似乎不起作用。有关最佳方法的任何想法吗?

这个问题围绕这个问题跳舞,但它已经过时了,没有人提供关于如何做到的实际答案:Can i randomize an array by providing a seed and get the same order? - “是” - 但是如何?

更新

到目前为止,答案适用于PHP 5.1和5.3,但不适用于5.2。就是这样,我想要运行它的机器正在使用5.2。

任何人都可以在不使用mt_rand的情况下举例说明吗?它在php 5.2中被“破坏”,因为它不会根据相同的种子给出相同的随机数序列。请参阅php mt_rand pagebug tracker以了解此问题。

11 个答案:

答案 0 :(得分:44)

  

抱歉,相应于the documentation   随机播放功能自动播种。

通常情况下,您不应该尝试使用自己的算法来随机化事物,因为它们很可能存在偏差。已知Fisher-Yates algorithm既高效又无偏见:

function fisherYatesShuffle(&$items, $seed)
{
    @mt_srand($seed);
    for ($i = count($items) - 1; $i > 0; $i--)
    {
        $j = @mt_rand(0, $i);
        $tmp = $items[$i];
        $items[$i] = $items[$j];
        $items[$j] = $tmp;
    }
}

示例(PHP 5.5.9):

php > $original = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
php > $shuffled = (array)$original;
php > fisherYatesShuffle($shuffled, 0);
php > print_r($shuffled);
Array
(
    [0] => 6
    [1] => 0
    [2] => 7
    [3] => 2
    [4] => 9
    [5] => 3
    [6] => 1
    [7] => 8
    [8] => 5
    [9] => 4
)
php > $shuffled = (array)$original;
php > fisherYatesShuffle($shuffled, 0);
php > print_r($shuffled);
Array
(
    [0] => 6
    [1] => 0
    [2] => 7
    [3] => 2
    [4] => 9
    [5] => 3
    [6] => 1
    [7] => 8
    [8] => 5
    [9] => 4
)

答案 1 :(得分:17)

您可以使用array_multisort按第二个mt_rand值数组对数组值进行排序:

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

mt_srand('123');
$order = array_map(create_function('$val', 'return mt_rand();'), range(1, count($arr)));
array_multisort($order, $arr);

var_dump($arr);

此处$order是一个mt_rand数组,其长度与$arr相同。 array_multisort$order的值进行排序,并根据$arr值的顺序对$order的元素进行排序。

答案 2 :(得分:4)

你遇到的问题是PHP内置了两个随机数生成器。

shuffle()命令不使用mt_rand()随机数生成器;它使用较旧的rand()随机数生成器。

因此,如果您希望shuffle()使用种子编号序列,则需要使用srand()而不是mt_srand()为旧版随机用户设定种子。

在大多数其他情况下,您应该使用mt_rand()而不是rand(),因为它是一个更好的随机数生成器。

答案 3 :(得分:1)

主要问题涉及两个部分。一个是关于如何洗牌。另一个是关于如何为其添加随机性。

一个简单的解决方案

这可能是主要问题的最简单答案。对于PHP脚本中的大多数情况来说已经足够了。但不是全部(见下文)。

function /*array*/ seedShuffle(/*one dimentional array*/ $array, /*integer*/ $seed) {
    $tmp = array();
    for ($rest = $count = count($array);$count>0;$count--) {
        $seed %= $count;
        $t = array_splice($array,$seed,1);
        $tmp[] = $t[0];
        $seed = $seed*$seed + $rest;
    }
    return $tmp;
}

即使它没有为所有可能的种子阵列组合产生真正的随机混洗,上述方法也会这样做。但是,如果你真的希望它是平衡的,那么我猜PHP不是你的选择。

高级程序员更有用的解决方案

如AndréLaszlo所述,随机化是一项棘手的工作。通常最好让专用对象处理它。我的观点是,当你编写shuffle函数时,你不需要为随机性而烦恼。根据你在shuffle中想要的ramdomness程度,你可能有许多PseudoRandom对象可供选择。因此,上面可能是这样的:

abstract class PseudoRandom {
    protected abstract function /*integer*/ nextInt();
    public function /*integer*/ randInt(/*integer*/ $limit) {
        return $this->nextInt()%$limit;
    }
}

function /*array*/ seedShuffle($array, /*PseudoRandom Object*/ $rnd) {
    $tmp = array();
    $count = count($array);
    while($count>0) {
        $t = array_splice($array,$rnd->randInt($count--),1);
        $tmp[] = $t[0];
    }
    return $tmp;
}

现在,这个解决方案是我投票的解决方案。它将随机码与随机码分开。根据您需要的随机类型,您可以继承PseudoRandom,添加所需的方法和首选公式。 并且,由于相同的随机函数可以与许多随机算法一起使用,因此可以在不同的地方使用一种随机算法。

答案 4 :(得分:1)

在最近的PHP版本中,播种PHP内置rand()mt_rand()函数不会每次都给出相同的结果。这个原因对我来说并不清楚(如果结果每次都不同,你为什么还想要播放函数。)无论如何,似乎唯一的解决办法就是write your own random function

class Random {

    // random seed
    private static $RSeed = 0;

    // set seed
    public static function seed($s = 0) {
        self::$RSeed = abs(intval($s)) % 9999999 + 1;
        self::num();
    }

    // generate random number
    public static function num($min = 0, $max = 9999999) {
        if (self::$RSeed == 0) self::seed(mt_rand());
        self::$RSeed = (self::$RSeed * 125) % 2796203;
        return self::$RSeed % ($max - $min + 1) + $min;
    }
}

用法:

// set seed
Random::seed(42);

// echo 10 numbers between 1 and 100
for ($i = 0; $i < 10; $i++) {
    echo Random::num(1, 100) . '<br />';
}

上面的代码会输出以下 的下一个序列:

76
86
14
79
73
2
87
43
62
7

只需更改种子就可以获得完全不同的种子&#34;随机&#34;序列

答案 5 :(得分:1)

一个变体也可以与PHP 7.2版一起使用,因为在最新的php版本中不推荐使用php函数create_function。

mt_srand($seed);

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

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

答案 6 :(得分:0)

我想这可以完成工作:

    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;

}

为了不限制结果数组,请将$ number_of_items_wanted设置为-1 为了不使用种子,请将$ seed设置为FALSE

答案 7 :(得分:0)

播种随机播放,同时保持键索引:

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);
}

答案 8 :(得分:0)

一个简单的解决方案:

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

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

它可能不像Fisher Yates shuffle那样随机,但是我发现它为我提供了足以满足我需要的熵。

答案 9 :(得分:0)

基于@ Gumbo,@ Spudley和@AndreyP答案,它的工作原理如下:

$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);

答案 10 :(得分:-2)

这对我来说似乎最简单......

srand(123);
usort($array,function($a,$b){return rand(-1,1);});