我正在寻找一个函数,我可以在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 page和bug tracker以了解此问题。
答案 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);});