Перетасовка массива на основе затравки для получения всегда одного и того же результата?
Мне нужно перетасовать массив на основе начального числа, чтобы я мог получить ту же самую перетасовку, если она мне понадобится.
Например:
1. print_r( shuffleIt( $array, 2 ) );
2. print_r( shuffleIt( $array, 6 ) );
3. print_r( shuffleIt( $array, 2 ) );
- и 3. покажет тот же перетасованный массив, но отличающийся от 2.
Я нашел эту функцию в гугле:
function entropy( $array, $sort_seed ) {
mt_srand( $sort_seed );
$order = array_map( create_function( '$val', 'return mt_rand( );' ), range( 1, count( $array ) ) );
array_multisort( $order, $array );
return $array;
}
Он отлично работает на моем ПК с php-cli, я всегда получаю один и тот же массив для каждого другого sort_seed, который я использую, но когда я загружаю его на сервер, я получаю разные массивы каждый раз, даже когда я использую один и тот же sort_seed.
Как я могу получить всегда один и тот же перетасованный массив при использовании одного и того же sort_seed?
Кстати. Мне нужно сохранить ключи или отсортировать многомерный массив, чтобы я мог хранить ключ там.
2 ответа:
Если вам это нужно на любом компьютере, вам нужен генератор случайных чисел, который работает одинаково на всех компьютерах. Я нашел один генератор псевдослучайных чисел, который может вам подойти, на странице Википедии генерация случайных чисел и поместил его в класс, чтобы вы могли его засеять.
Я не знаю, для чего вам это нужно, но это просто может удовлетворить ваши потребности. Он не зависит от конфигурации системы:function shuffleIt($array, $seed) { $mwc = new mwc($seed); $order = array(); $count = count($array); while($count--) $order[] = $mwc->random() ; array_multisort($order, $array); return $array; } /** * Multiply-with-carry RNG * * method invented by George Marsaglia */ class mwc { private static $def_m_w = 1712; /* must not be zero */ private static $def_m_z = 23; /* must not be zero */ private $m_w, $m_z; public function __construct($seed = NULL) { $this->m_w = self::$def_m_w; $this->m_z = self::$def_m_z; if (NULL !== $seed) $this->seed($seed); } public function seed($seed) { $seed = (int) $seed; if (!$seed) throw new InvalidArgumentException('Must not be zero.'); $this->m_z = $seed; $this->random(); } public function random() { $this->m_z = 36969 * ($this->m_z & 65535) + ($this->m_z >> 16); $this->m_w = 18000 * ($this->m_w & 65535) + ($this->m_w >> 16); return ($this->m_z << 16) + $this->m_w; /* 32-bit result */ } }
Примечание: это может вести себя по-разному между 32/64 битами системы, тем более что PHP отличается здесь для целых чисел и переполнений между windows и unix. Вы можете захотеть смещение по знаковому минимуму для 32-битных целых чисел в PHP вместо 0, как сейчас, чтобы переключить реализацию на gmp или просто уменьшить размер на один бит.
Пример использования 32 бит, о работе которого сообщил Экке из Нидерландов
$shuffle = new GeorgeShuffle(); $seed = $shuffle->seed(); $a = array('A', 'B', 'C', 'D', 'E', 'F', 'G'); $shuffle->reOrder($a); var_dump($a); $shuffle->seed($seed); $shuffle->reOrder($a); var_dump($a); /** * Array shuffle class using * the multiply-with-carry method * invented by George Marsaglia */ class GeorgeShuffle { private static $def_m_w = 1959; /* must not be zero */ private static $def_m_z = 2006; /* must not be zero */ private $m_w, $m_z; const maxint = 2147483647; public function __construct($seed = null) { $this->m_w = self::$def_m_w; $this->m_z = self::$def_m_z; if ($seed) $this->seed($seed); } public function reOrder(&$array, $seed = null) { if (!empty($seed)) $this->seed($seed); $a = array(); for ($i = 0, $j = count($array); $i < $j; $i++) { $a[$i] = $this->random(); } array_multisort($a, $array); //- to return a copy, remove the & return $array; } public function seed($seed = false) { if (is_string($seed)) $seed = hexdec($seed); if (empty($seed)) $seed = round(mt_rand(1, self::maxint)); $this->m_z = $seed; $this->random(); //- return the seed used in hex (8 chars) for reproducing return str_pad(dechex($seed), 8, '0', STR_PAD_LEFT); } public function random() { $this->m_z = 36969 * (($this->m_z And 65535) + ($this->m_z >> 16)); $this->m_w = 18000 * (($this->m_w And 65535) + ($this->m_w >> 16)); return ($this->m_z << 16) + $this->m_w; /* 32-bit signed result */ } }
Вы случайно не запускаете расширение безопасностиSuhosin ? По-видимому, у него есть пара директив, которые не позволяют скриптам устанавливать семя:
suhosin.srand.ignore = On suhosin.mt_srand.ignore = On
Дальнейшее чтение:
- http://www.suspekt.org/2008/08/22/suhosin-0926-improved-randomness/
- http://www.adayinthelifeof.nl/2010/12/13/php-srand-problems-with-suhosin/
Вот быстрый способ проверить, является ли это проблемой:
<?php mt_srand(33); var_dump(mt_rand(1, 10000)); mt_srand(33); var_dump(mt_rand(1, 10000)); mt_srand(33); var_dump(mt_rand(1, 10000));