PHPで配列とシードを渡して、「ランダム化された」配列を取得できる関数を探しています。同じ配列と同じシードをもう一度渡した場合は、同じ出力を取得します。
私はこのコードを試しました
//サンプル配列 $ test = array(1,2,3,4,5,6); //配列を表示 print_r($ test); //乱数ジェネレーターをシード mt_srand( '123'); //それに基づいて乱数を生成する echo mt_Rand(); echo "\ n"; //配列をシャッフル shuffle($ test); //結果を表示 print_r($ test);
しかし、それは機能していないようです。これを行うための最良の方法について何か考えはありますか?
この質問は問題の周りで踊りますが、それは古く、それを行う方法について実際の答えを誰も提供していません: シードを提供して同じ順序を取得することで配列をランダム化できますか? -「はい」-しかしどうやって?
これまでの答えは、PHP 5.1および5.3で機能しますが、5.2では機能しません。これを実行したいマシンで5.2を使用しているだけです。
Mt_randを使用せずに例を挙げられる人はいますか? php 5.2では、同じシードに基づいて同じ乱数シーケンスが得られないため、「壊れています」。この問題については、 php mt_Rand page および バグトラッカー を参照してください。
array_multisort
を使用して、配列値を mt_Rand
値の2番目の配列で並べ替えることができます。
$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
の値の順序に従って並べ替えます。
ドキュメント に応じて、シャッフル関数は自動的にシードされます。
通常、物事が偏っている可能性が非常に高いので、物事をランダム化する独自のアルゴリズムを考え出そうとしないでください。 Fisher-Yatesalgorithm は、効率的で偏りがないことが知られています。
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
)
あなたが持っている問題は、PHP=に2つの乱数ジェネレータが組み込まれていることです。
shuffle()
コマンドは、mt_Rand()
乱数ジェネレーターを使用しません。古いRand()
乱数ジェネレータを使用します。
したがって、shuffle()
でシードされた数列を使用する場合は、srand()
ではなくmt_srand()
を使用して古いランダマイザーをシードする必要があります。
他のほとんどの場合、mt_Rand()
ではなくRand()
を使用する必要があります。これは、より優れた乱数ジェネレーターであるためです。
主な質問には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つのランダムアルゴリズムを別の場所で使用できます。
Php関数create_functionは最新のphpバージョンで非推奨になっているため、PHPバージョン7.2でも機能するバリアント。
mt_srand($seed);
$getMTRand = function () {
return mt_Rand();
};
$order = array_map($getMTRand, range(1, count($array)));
array_multisort($order, $array);
return $array;
これでうまくいくと思います:
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に設定します。
最近の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
シードを変更するだけで、完全に異なる「ランダム」シーケンスを取得できます
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);
}