35

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

我试过这段代码

//样本数组
$test = 数组(1,2,3,4,5,6);
//显示数组
print_r($test);

//种子随机数生成器
mt_srand('123');
//根据它生成一个随机数
回声 mt_rand();
回声“\n”;

//打乱数组
洗牌($测试);

//显示结果
print_r($test);

但这似乎不起作用。对最好的方法有什么想法吗?

这个问题围绕着这个问题展开,但它已经过时了,没有人提供关于如何做到这一点的实际答案:我可以通过提供种子来随机化一个数组并获得相同的顺序吗?- “是的,但如何?

更新

到目前为止的答案适用于 PHP 5.1 和 5.3,但不适用于 5.2。恰巧我要运行它的机器使用的是 5.2。

谁能在不使用 mt_rand 的情况下举个例子?它在 php 5.2 中被“破坏”了,因为它不会基于相同的种子给出相同的随机数序列。请参阅php mt_rand 页面错误跟踪器以了解此问题。

4

11 回答 11

50

抱歉,但根据文档,随机播放功能会自动播种。

通常,您不应该尝试提出自己的算法来随机化事物,因为它们很可能是有偏见的。众所周知,Fisher-Yates 算法既高效又无偏见:

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
)
于 2011-07-02T15:26:29.490 回答
22

您可以使用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$arrarray_multisort$order$arr$order

于 2011-07-02T15:27:23.890 回答
5

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

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

因此,如果您想shuffle()使用种子编号序列,您需要使用srand()而不是mt_srand().

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

于 2011-07-02T15:23:15.840 回答
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 函数时,你不应该为随机性而烦恼。根据您希望随机播放的随机程度,您可能有许多 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 子类化,添加所需的方法和您喜欢的公式。并且,由于同一个shuffle函数可以与许多随机算法一起使用,因此可以在不同的地方使用一种随机算法。

于 2014-03-29T09:21:13.893 回答
1

在最近的 PHP 版本中,植入 PHP 内置函数rand()mt_rand()函数不会每次都给您相同的结果。我不清楚这样做的原因(如果每次结果都不同,为什么还要为函数播种。)无论如何,似乎唯一的解决方案是编写自己的随机函数

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

只需更改种子即可获得完全不同的“随机”序列

于 2015-04-11T13:33:05.753 回答
1

一个也适用于 PHP 7.2 的变体,因为 php 函数 create_function 在最新的 php 版本中已被弃用。

mt_srand($seed);

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

$order = array_map($getMTRand, range(1, count($array)));
array_multisort($order, $array);
return $array;
于 2018-08-06T14:59:10.530 回答
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

于 2019-05-23T22:36:07.500 回答
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);
}
于 2019-06-12T22:21:42.383 回答
0

一个简单的解决方案:

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

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

它可能不像Fisher Yates shuffle那样随机,但我发现它给了我足够多的熵来满足我的需要。

于 2020-08-27T20:06:32.500 回答
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);
于 2020-11-18T09:30:39.683 回答
-2

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

srand(123);
usort($array,function($a,$b){return rand(-1,1);});
于 2016-03-29T14:46:31.533 回答