以前にも estab_table というクラスを組むには組んでいたのですが。
ほんのりと思うところがありまして、新規に組んでみました。
https://github.com/gallu/MagicWeapon/blob/master/probability.inc
https://github.com/gallu/MagicWeapon/blob/master/probability_T.php (超大まかな使い方)
基本的には
・確率はすべて整数で入れる
・別に「トータルが100であるかどうか」は気にしない(のでデータ作るときにでもチェックしてくれぃ)
という感じ。
pushメソッドの第二引数「確率」の合計は、100でもいいし、1000でもいいし、って感じ。まぁ昨今の「0.002%」とかってのを受け入れると合計の数値が100,000とかになっちゃうんだろうけど、まぁそれはそれで。
使い方としては
・pushで一通り突っ込んで
・choiceで取り出す
だけなのですが。ロジック上、pushは「確率が高いものから順番にpushする」と、幾分効率がよくなると思われたりします。
大体がちゃって「マスターがテーブル上にある」ケースが多いので。引っ張ってくる時にorder byでもしていただければ、って感じかねぇ。
で、以下備忘録的に少し。
まずは確率の検証。モンテカルロ法的な手法をとってみます。
<?php require_once('probability.inc'); $obj = new probability(); // $obj->push('item_1', 50); $obj->push('item_2', 30); $obj->push('item_3', 20); //var_dump($obj); // $data = array(); // for($i = 0; $i < 100000; $i ++) { @$data[ $obj->choice() ] += 1; } // printf("item_1 is %.4f\n", $data['item_1'] / 100000 * 100); printf("item_2 is %.4f\n", $data['item_2'] / 100000 * 100); printf("item_3 is %.4f\n", $data['item_3'] / 100000 * 100);
あるタイミングにおける結果は、こんな感じでした。
[gallu@localhost wk]$ php probability_T2.php
item_1 is 50.1870
item_2 is 29.7750
item_3 is 20.0380
[gallu@localhost wk]$ php probability_T2.php
item_1 is 49.5910
item_2 is 30.3550
item_3 is 20.0540
[gallu@localhost wk]$ php probability_T2.php
item_1 is 49.9310
item_2 is 29.9220
item_3 is 20.1470
[gallu@localhost wk]$ php probability_T2.php
item_1 is 50.0470
item_2 is 29.9730
item_3 is 19.9800
[gallu@localhost wk]$ php probability_T2.php
item_1 is 50.1910
item_2 is 29.7650
item_3 is 20.0440
まぁ大体意図通りだねぇ、っと。
次に性能。
<?php require_once('probability.inc'); $t = microtime(true); for($j = 0; $j < 1000; $j ++) { $obj = new probability(); // for($i = 0; $i < 200; $i ++) { $obj->push("item_{$i}", 1); } $obj->choice(); } $t = microtime(true) - $t; var_dump($t);
これで「1000回」ぶん回してみませう。1000回だとちょうど「1秒が実際には1ミリ秒」になるからわかりやすいのよ、的な(笑
実際に「1つのがちゃに200種類」は些か盛りすぎな気がせんでもないのですが、まぁそれくらいが確認にはよろしかろう、的な。
ノーマル
[gallu@localhost wk]$ php probability_TTT.php
float(1.2731449604034)
[gallu@localhost wk]$ php probability_TTT.php
float(1.272558927536)
[gallu@localhost wk]$ php probability_TTT.php
float(1.2700932025909)
[gallu@localhost wk]$ php probability_TTT.php
float(1.2739930152893)
[gallu@localhost wk]$ php probability_TTT.php
float(1.2681269645691)
実質的には大体1.27ミリ秒。気にならないで済む程度の速度なのではないかなぁ、と。
念の為に「配列の一番最後」を意図的にチョイスするように小細工した時の速度。
[gallu@localhost wk]$ php probability_TTT.php
float(1.3437149524689)
[gallu@localhost wk]$ php probability_TTT.php
float(1.3538720607758)
[gallu@localhost wk]$ php probability_TTT.php
float(1.3482151031494)
[gallu@localhost wk]$ php probability_TTT.php
float(1.343001127243)
[gallu@localhost wk]$ php probability_TTT.php
float(1.3433270454407)
それでも1.34ミリ秒。
せっかくなんで「配列の先頭」を意図的にチョイスするように小細工した時の速度。
[gallu@localhost wk]$ php probability_TTT.php
float(1.2022018432617)
[gallu@localhost wk]$ php probability_TTT.php
float(1.2001230716705)
[gallu@localhost wk]$ php probability_TTT.php
float(1.1996169090271)
[gallu@localhost wk]$ php probability_TTT.php
float(1.2015750408173)
[gallu@localhost wk]$ php probability_TTT.php
float(1.1977519989014)
1.2ミリ秒。
まぁまぁ。
もうちょっと速度を気にするんなら、もしかするとだけど「あらかじめpushしたインスタンス」をシリアライズしておいてDBにぶち込んで、実際に使う時には「アンシリアライズして使う」とかやると、もっと早いかも…と思ったのでさっそく検証。
<?php require_once('probability.inc'); $obj = new probability(); // for($i = 0; $i < 200; $i ++) { $obj->push("item_{$i}", 1); } $s = serialize($obj); $obj = null; $t = microtime(true); for($j = 0; $j < 1000; $j ++) { $obj = unserialize($s); $obj->choice(); } $t = microtime(true) - $t; var_dump($t);
結果
[gallu@localhost wk]$ php probability_TTT2.php
float(0.24353098869324)
[gallu@localhost wk]$ php probability_TTT2.php
float(0.23857712745667)
[gallu@localhost wk]$ php probability_TTT2.php
float(0.24080300331116)
[gallu@localhost wk]$ php probability_TTT2.php
float(0.24097180366516)
[gallu@localhost wk]$ php probability_TTT2.php
float(0.24446702003479)
アベレージ、0.24ミリ秒。
む、思った以上に早いでやんの。
「多少の運用コストかけてでもマシンコストを下げたい」ってニーズであれば、こっちのほうがいいかもしんまい…けどまぁ「微々たるもん」って気もするので、その辺はお好みかなぁ。
「ユーザ毎に確率を変える」とかその手の小細工が必要になるとこのコードは使えないのですが*1、そうでなければ、割と使えるんじゃないかなぁ、という感触。
まぁ以前も似たようなコードを組んでたのですが、なんかふと「降りてきた」ので、一気に書き上げてみましたとさ。
感想その他ありましたらお気軽に。
*1:それをやっていいのか悪いのかはまた別問題 B-p