35

PHP で配列とシードを渡して、「ランダム化された」配列を取得できる関数を探しています。同じ配列と同じシードを再度渡すと、同じ出力が得られます。

私はこのコードを試しました

//サンプル配列
$test = 配列(1,2,3,4,5,6);
//配列を表示
print_r($テスト);

// 乱数ジェネレーターをシードします
mt_srand('123');
// それに基づいて乱数を生成する
echo mt_rand();
エコー "\n";

//配列をシャッフルする
シャッフル($テスト);

//結果を表示
print_r($テスト);

しかし、うまくいかないようです。これを行う最善の方法について何か考えはありますか?

この質問は問題を回避しますが、それは古く、誰もそれを行う方法について実際の回答を提供していません:シードを提供して配列をランダム化し、同じ順序を取得できますか? - 「はい」 - でもどうやって?

アップデート

これまでの回答は、PHP 5.1 および 5.3 では機能しますが、5.2 では機能しません。たまたまこれを実行したいマシンが5.2を使用しています。

mt_rand を使わずに例を挙げることはできますか? 同じシードに基づいて同じ乱数シーケンスを提供しないため、php 5.2 では「壊れています」。この問題については、php mt_rand ページバグ トラッカーを参照してください。

4

11 に答える 11

50

申し訳ありませんが、ドキュメントによると、シャッフル関数は自動的にシードされます。

通常、物事をランダム化するために独自のアルゴリズムを考え出そうとするべきではありません。ただし、Fisher-Yates アルゴリズムは効率的で偏りがないことが知られています。

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
)
于 2011-07-02T15:26:29.490 に答える
22

array_multisort値の 2 番目の配列で配列値を並べ替えるために使用でき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$arrarray_multisort$order$arr$order

于 2011-07-02T15:27:23.890 に答える
5

問題は、PHP に 2 つの乱数ジェネレーターが組み込まれていることです。

このコマンドは乱数発生器shuffle()を使用しません。mt_rand()古いrand()乱数ジェネレーターを使用します。

したがって、シードされた数列を使用する場合は、ではなくshuffle()を使用して、古いランダマイザーをシードする必要があります。srand()mt_srand()

それ以外のほとんどの場合、より優れた乱数ジェネレータであるため、mt_rand()ではなくを使用する必要があります。rand()

于 2011-07-02T15:23:15.840 に答える
1

最近の 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

シードを変更するだけで、まったく異なる「ランダム」シーケンスが得られます

于 2015-04-11T13:33:05.753 に答える
1

主な質問には 2 つの部分があります。1つは、シャッフルの仕方についてです。もう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 が述べたように、ランダム化はトリッキーな作業です。通常は、専用のオブジェクトに処理させるのが最善です。私のポイントは、シャッフル関数を書くときにランダム性を気にする必要はないということです。シャッフルで必要な乱数の程度に応じて、多数の 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 をサブクラス化し、必要なメソッドと好みの式を追加できます。また、同じシャッフル関数が多くのランダム アルゴリズムで使用される可能性があるため、1 つのランダム アルゴリズムが異なる場所で使用される可能性があります。

于 2014-03-29T09:21:13.893 に答える
-2

これは私にとって最も簡単なようです...

srand(123);
usort($array,function($a,$b){return rand(-1,1);});
于 2016-03-29T14:46:31.533 に答える