gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

おおざっぱに「がちゃ用の選択プログラム」を組んでみた

以前にも 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