ガチャのロジックをいくつか見てきたけど、どれがいいか検証してみた。
主に2種類あって、以下の通り
ロジックの種類
- 確率の数だけIDのリストを積んで、shuffleして1件取得する方法
- 確率の累計を求めてmt_randで選択
案1
$data = self::getData(); // CSVからデータ取得
$list = array();
foreach ($data as $record) {
// 確率の個数分$listに積んでいく
for ($i = 0 ; $i < $record['rate'] ; $i++) {
$list[] = $record['id'];
}
}
// リストを混ぜる
shuffle($list);
// リストの先頭1件を取得
return array_shift($list);
案2
$data = self::getData(); // CSVからデータ取得
// 確率の合計値を求める
$max = 0;
foreach ($data as $record) {
$max += $record['rate'];
}
// 0から始まるので合計値から1を引いた数までの乱数を取得
$rate = mt_rand(0, $max-1);
// 乱数がどこに当たるか調べていく
foreach ($data as $record) {
$max -= $record['rate'];
if ($max <= $rate) {
return $record['id'];
}
}
throw new Exception('ここには来ないはず!');
データ(ID,確率)
1,10
2,10
3,10
:
:
50,1
51,2
52,3
53,4
計測結果
計測の種類として以下の観点でのパターンを用意し、指定回数ガチャを引くのを10回施行した平均をとりました。
- IDの増減
- 確率の数値の大小
IDの数 | 確率の数値 | ガチャ回数 | 案1 | 案2 |
---|---|---|---|---|
100 | 1-100 | 10000回 | 19.7sec | 1.5sec |
100 | 10000-20000 | 10000回 | 30sec以上 | 1.5sec |
100000 | 1-100 | 100回 | 47.0sec | 1.9sec |
100000 | 10000-20000 | 100回 | メモリ溢れ | 30sec以上 |
※上記、案1のshuffleはrand、案2はmt_randなので正確なベンチではありません。
(mpywさんのコメントの記事参照。情報ありがとうございます。)
案2の圧勝ですね。結果は予想通りだったでしょうか?
何でこれをやったかって?
そりゃあ、案1の方が速いと譲らないPLがいたからですよ。。。
他にも違うパターンを見つけたら、それも試してみたいと思います。
(最初にSR,R,Nなどの引くランクを求めて、対応したガチャを引く系は除外)
2015/12/24追記
shuffleはrandを利用しているらしいです。
なので、ベンチマークをより正確にするならば以下のロジックとする必要がありますね
PHP7ではrandom_intという関数ができたようです。PHP7環境の場合はこちらを利用するものよいかと。
- 案2のmt_randをrandにする
- 案1のshuffleをmt_randを使うようなメソッド作る
とはいえ、大きな差が出てるので、結果は覆らないと思いますが。。。
mpywさん情報ありがとうございました。
http://qiita.com/mpyw/items/c644dab7d067626fc629