Перемешивать массив на основе семян, чтобы получить всегда один и тот же результат? - PullRequest
3 голосов
/ 14 октября 2011

Мне нужно перетасовать массив на основе начального числа, чтобы я мог получить тот же случай перемешивания, если он мне нужен.

Например:

1. print_r( shuffleIt( $array, 2 ) );
2. print_r( shuffleIt( $array, 6 ) );
3. print_r( shuffleIt( $array, 2 ) );
  1. и 3.будет показывать тот же массив в случайном порядке, но отличается от 2.

Я нашел эту функцию googling:

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 ]

2 голосов
/ 14 октября 2011

Если вам это нужно на любом компьютере, вам нужен генератор случайных чисел, который работает одинаково на всех компьютерах. Я нашел один генератор псевдослучайных чисел, который вам подходит, со страницы *1002* * Википедии генерации случайных чисел и поместил его в класс, чтобы вы могли его заполнить.

Я не знаю, для чего вам это нужно, но это может просто удовлетворить ваши потребности. Это не зависит от конфигурации системы:

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 отличается здесь для целых чисел и переполнений между окнами и Unix. Возможно, вы захотите сместить минимум со знаком для 32-битных целых чисел в PHP вместо 0, как сейчас, чтобы переключить реализацию на gmp или просто уменьшить размер на один бит.


Пример использования 32-битного сообщения о работе ekke из Нидерландов

$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 */
    }
}
1 голос
/ 14 октября 2011

Является ли у вас случайно установленное расширение безопасности Suhosin ? Очевидно, у него есть пара директив, которые не позволяют сценариям устанавливать начальное число:

suhosin.srand.ignore = On
suhosin.mt_srand.ignore = On

Дополнительная литература:

Вот быстрый способ проверить, является ли это проблемой:

<?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));
...