Рандомизировать массив PHP с начальным числом? - PullRequest
32 голосов
/ 02 июля 2011

Я ищу функцию, в которую я могу передать массив и начальное число в 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 page и bug tracker , чтобы узнать об этой проблеме.

Ответы [ 9 ]

45 голосов
/ 02 июля 2011

Извините, но в соответствии с документацией функция случайного выбора добавляется автоматически.

Обычно вам не следует пытаться придумать свои собственные алгоритмы длярандомизируйте вещи, так как они очень вероятно будут предвзятыми.Известно, что алгоритм Фишера-Йейтса эффективен и беспристрастен:

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
)
19 голосов
/ 02 июля 2011

Вы можете использовать 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.

4 голосов
/ 02 июля 2011

Проблема в том, что в PHP встроены два генератора случайных чисел.

Команда shuffle() не использует генератор случайных чисел mt_rand(); он использует более старый rand() генератор случайных чисел.

Поэтому, если вы хотите, чтобы shuffle() использовала последовательность чисел с сеяным числом, вам нужно запустить более рандомизатор, используя srand() вместо mt_srand().

В большинстве других случаев вам следует использовать mt_rand() вместо rand(), так как это лучший генератор случайных чисел.

1 голос
/ 06 августа 2018

Вариант, который также работает с версией 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;
1 голос
/ 11 апреля 2015

В последних версиях 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

Просто изменитесемя, чтобы получить совершенно другую «случайную» последовательность

1 голос
/ 29 марта 2014

Основной вопрос состоит из двух частей. Один о том, как перемешать. Другой вопрос о том, как добавить к нему случайность.

Простое решение

Это, наверное, самый простой ответ на главный вопрос. Это достаточно для большинства случаев в сценариях 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 не будет вашим выбором.

Более полезное решение для продвинутых программистов

Как заявил Андре Ласло, рандомизация - сложное дело. Обычно лучше всего позволить выделенному объекту обрабатывать его. Я хочу сказать, что вам не нужно беспокоиться о случайности, когда вы пишете функцию перемешивания. В зависимости от того, какой степени случайности вы хотели бы использовать в случайном порядке, у вас может быть несколько объектов 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, добавить необходимые методы и предпочитаемые формулы. И, поскольку одна и та же функция случайного выбора может использоваться со многими случайными алгоритмами, один случайный алгоритм может использоваться в разных местах.

0 голосов
/ 13 июня 2019

Сеялка в случайном порядке при сохранении индекса ключа:

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);
}
0 голосов
/ 24 мая 2019

Полагаю, это сработает:

    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 * 1004.*

0 голосов
/ 29 марта 2016

Это кажется самым легким для меня ...

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