Help us understand the problem. What is going on with this article?

PHPでTwitterの診断メーカーで使われているような「入力値に依存する乱数」を生成する方法

More than 3 years have passed since last update.

基本

mt_randのシードを入力文字列のハッシュ値で固定すればいいじゃん!」 と真っ先に思いつくんですが,mt_srand の引数には整数しか渡せないので,md5sha1などはそのままでは適用できません.crc32関数がちょうど整数型でハッシュ値を返してくれる関数なので,今回はこれを利用することにします.

※ 32bit環境と64bit環境で得られる結果が異なるので注意してください

PHP5.4以降
/**
 * @param string $seed シード値
 * @param int $min 最小値
 * @param int $max 最大値
 * @param bool $daily 日付毎にランダマイズするか
 * @return int 範囲内の乱数
 */
function random_int_by_seed($seed, $min, $max, $daily = false) {
    $seed = (string)filter_var($seed);
    $min = (int)$min;
    $max = (int)$max;
    if ($max < $min) {
        throw new \InvalidArgumentException('Invalid range');
    };
    if ($daily) {
        $seed .= (new \DateTime('today Asia/Tokyo'))->format('Y/m/d');
    }
    mt_srand(crc32($seed));
    $r = mt_rand($min, $max);
    mt_srand();
    return $r;
}
バラつき度の確認
$counts = array_fill(0, 5, 0);
for ($i = 0; $i < 10000; ++$i) {
    ++$counts[random_int_by_seed(openssl_random_pseudo_bytes(100), 0, 4)];
}
var_dump($counts);

/*
array(5) {
  [0]=>
  int(2002)
  [1]=>
  int(2037)
  [2]=>
  int(1968)
  [3]=>
  int(2023)
  [4]=>
  int(1970)
}
*/

応用

実際は,乱数に応じて複数の選択肢を選んでいくことになるはずなので,これに適応できるようにちょっと書き加えます.

PHP5.4以降
/**
 * @param string $seed シード値
 * @param array $table 「選択肢の配列」の配列
 * @param bool $daily 日付毎にランダマイズするか
 * @return array 「選ばれた選択肢」の配列
 */
function random_items_by_seed($seed, array $table, $daily = false) {
    $seed = (string)filter_var($seed);
    if ($daily) {
        $seed .= (new \DateTime('today Asia/Tokyo'))->format('Y/m/d');
    }
    mt_srand(crc32($seed));
    $r = [];
    foreach ($table as $i => $row) {
        $row = (array)$row;
        $r[$i] = $row ? $row[mt_rand(0, count($row) - 1)] : null;
    };
    mt_srand();
    return $r;
}
使用例
$name = 'mpyw';
$r = random_items_by_seed($name, [
    ['大吉', '吉', '中吉', '小吉', '末吉', '凶', '大凶'],
    ['赤', '青', '緑', 'ピンク'],
], true);
vprintf("{$name}さんの今日の運勢は%sです!ラッキーカラーは%sです!\n", $r);

/*
mpywさんの今日の運勢は末吉です!ラッキーカラーは緑です!
*/

なお,シード値が配列の複数要素になる場合にはserializejson_encodeしたものを$seedとして渡せばいいでしょう.

mpyw
古い記事はそのまま参考にしないようにご注意ください
synapse
Synapseは、オンラインサロンサービスにおけるパイオニアとして、かつて存在していたスタートアップです。
https://synapseam.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした