用种子随机化 PHP 数组?

发布于 2024-11-18 13:43:27 字数 916 浏览 2 评论 0原文

我正在寻找一个可以在 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 byproviding a Seed并得到相同的订单? - “是” - 但如何呢?

更新

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

任何人都可以举一个不使用 mt_rand 的例子吗?它在 php 5.2 中被“破坏”,因为它不会根据相同的种子给出相同的随机数序列。请参阅 php mt_rand 页面错误跟踪器 了解此问题。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

很酷又爱笑 2024-11-25 13:43:27

抱歉,但根据
文档

随机播放功能会自动播种。

通常,您不应该尝试提出自己的算法来随机化事物,因为它们很可能存在偏见。 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
)

Sorry, but accordingly to the
documentation
the
shuffle function is seeded automatically.

Normally, you shouldn't try to come up with your own algorithms to randomize things since they are very likely to be biased. The Fisher-Yates algorithm is known to be both efficient and unbiased though:

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

Example (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
)
一口甜 2024-11-25 13:43:27

您可以使用 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);

这里 $ordermt_rand< 的数组/code> 相同的值长度为$arrarray_multisort$order 的值进行排序,并根据 $order 的值的顺序对 $arr 的元素进行排序代码>.

You can use array_multisort to order the array values by a second array of mt_rand values:

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

Here $order is an array of mt_rand values of the same length as $arr. array_multisort sorts the values of $order and orders the elements of $arr according to the order of the values of $order.

囚我心虐我身 2024-11-25 13:43:27

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

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

因此,如果您希望 shuffle() 使用种子数字序列,则需要使用 srand() 而不是 mt_srand()< /代码>。

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

The problem you have is that PHP comes with two random number generators built in.

The shuffle() command does not use the mt_rand() random number generator; it uses the older rand() random number generator.

Therefore, if you want shuffle() to use a seeded number sequence, you need to seed the older randomiser, using srand() rather than mt_srand().

In most other cases, you should use mt_rand() rather than rand(), since it is a better random number generator.

奢欲 2024-11-25 13:43:27

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

一个简单的解决方案

这可能是主要问题的最简单的答案。对于 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 进行子类化,添加所需的方法和您喜欢的公式。
并且,由于相同的洗牌函数可以与多种随机算法一起使用,因此一种随机算法可以在不同的地方使用。

The main question involves two parts. One is about how to shuffle. The other is about how to add randomness to it.

A simple solution

This is probably the simplest answer to the main question. It is sufficient for most cases in PHP scripting. But not all (see below).

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

The above method will do, even though it doesn't produce true random shuffles for all possible seed-array combinations. However, if you really want it to be balanced and all, I guess PHP shuldn't be your choice.

A more useful solution for advanced programmers

As stated by André Laszlo, randomization is a tricky business. It is usually best to let a dedicated object handle it. My point is, that you shouldn't need to bother with the randomness when you write the shuffle function. Depending on what degree of ramdomness you would like in your shuffle, you may have a number of PseudoRandom objects to choose from. Thus the above could look like this:

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

Now, this solution is the one I would vote for. It separates shuffle codes from randomization codes. Depending on what kind of random you need you can subclass PseudoRandom, add the needed methods and your preferred formulas.
And, as the same shuffle function may be used with many random algorithms, one random algorithm may be used in different places.

分開簡單 2024-11-25 13:43:27

在最近的 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

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

In recent PHP versions, seeding the PHP builtin rand() and mt_rand() functions will not give you the same results everytime. The reason for this is not clear to me (why would you want to seed the function anyway if the result is different every time.) Anyway, it seems like the only solution is to 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;
    }
}

Usage:

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

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

The code above will output the folowing sequence every time you run it:

76
86
14
79
73
2
87
43
62
7

Just change the seed to get a completely different "random" sequence

仲春光 2024-11-25 13:43:27

该变体也适用于 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;

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;
如歌彻婉言 2024-11-25 13:43:27

我想这可以完成工作:

    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

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

古镇旧梦 2024-11-25 13:43:27

在保持关键索引的同时进行种子洗牌:

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

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);
}
凹づ凸ル 2024-11-25 13:43:27

一个简单的解决方案:

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

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

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

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.

撩起发的微风 2024-11-25 13:43:27

根据@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);

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);
剩余の解释 2024-11-25 13:43:27

自制函数,使用crc32(注意:也返回负值(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>';

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>';
倾其所爱 2024-11-25 13:43:27

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

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

This seems the easiest for me...

srand(123);
usort($array,function($a,$b){return rand(-1,1);});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文