Рандомизировать массив PHP с семенем?
Я ищу функцию, в которую я могу передать массив и начальное число в 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);
Но это не похоже на работу. Есть мысли о том, как это сделать?
Этот вопрос танцует вокруг проблемы, но он старый, и никто не дал фактического ответа о том, как это сделать: могу ли я рандомизировать массив, предоставив начальное число и получив тот же порядок? - "Да, но как?
Обновить
Пока ответы работают с PHP 5.1 и 5.3, но не с 5.2. Так получилось, что машина, на которой я хочу запустить, использует 5.2.
Кто-нибудь может привести пример без использования mt_rand? Он "сломан" в php 5.2, потому что он не даст одинаковую последовательность случайных чисел, основанную на одном и том же семени. Посетите страницу php mt_rand и систему отслеживания ошибок, чтобы узнать об этой проблеме.
Ответы
Ответ 1
Вы можете использовать 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
Извините, но в соответствии с документацией функция shuffle добавляется автоматически.
Обычно, вы не должны пытаться придумать свои собственные алгоритмы для рандомизации вещей, так как они, скорее всего, будут предвзятыми. Известно, что алгоритм Фишера-Йейтса эффективен и беспристрастен:
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
)
Ответ 3
Проблема заключается в том, что PHP поставляется с двумя генераторами случайных чисел.
Команда shuffle()
не использует генератор случайных чисел mt_rand()
; он использует более старый генератор случайных чисел rand()
.
Поэтому, если вы хотите, чтобы shuffle()
использовала последовательность семенных чисел, вам нужно выровнять старший рандомизер, используя srand()
, а не mt_srand()
.
В большинстве других случаев вы должны использовать mt_rand()
, а не rand()
, так как это лучший генератор случайных чисел.
Ответ 4
Основной вопрос состоит из двух частей. Один из них о том, как перетасовать. Другой - о том, как добавить к нему случайность.
Простое решение
Это, вероятно, самый простой ответ на главный вопрос. Достаточно для большинства случаев 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 не будет вашим выбором.
Более полезное решение для продвинутых программистов
Как заявил Андре Ласло, рандомизация - сложный бизнес. Как правило, лучше всего позволить выделенному объекту обрабатывать его. Я хочу сказать, что вам не нужно беспокоиться о случайности, когда вы пишете функцию тасования. В зависимости от того, какую степень 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, добавить необходимые методы и ваши предпочтительные формулы.
И, поскольку одна и та же функция тасования может использоваться со многими случайными алгоритмами, один случайный алгоритм может использоваться в разных местах.
Ответ 5
В последних версиях 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
Просто измените семя, чтобы получить совершенно другую "случайную" последовательность
Ответ 6
Вариант, который также работает с 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;
Ответ 7
Я думаю, что это сделает работу:
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.
Ответ 8
Посеянное перемешивание при сохранении индекса ключа:
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);
}
Ответ 9
Это кажется мне самым легким...
srand(123);
usort($array,function($a,$b){return rand(-1,1);});