前に自作ゲームを作っており、ガチャっぽいものを作成しようとしたときに、
本来はデータベースから結果等を取得する必要があるのですが、
プログラムだけで実現できないかと検討した結果を備忘録として残します
また、追加でDLLを追加したりせず、既存の機能のみで作成しています
C++ではMap、C#ではDictionaryで作成できます。今回の例はすべてC++になります
いくつかの中から結果をひとつだけ返却する
全確率の累計値を算出
確率の累計値を求めておきます、後ほど使用します
for (auto per = targetDict.begin(); per != targetDict.end(); per++)
{
total += per->second;
}
累計確率までの乱数を生成
上記で求めた累計値を上限とした値の乱数生成する
C++ではメルセンヌ・ツイスターという理論で乱数を作成しています
メルセンヌ・ツイスターについては以下参考リンク先を確認してください
-
メルセンヌ・ツイスターは必要か? #アルゴリズム - Qiita
#今回の話題人類にMTはでかすぎる#疑似乱数とはコンピュータでは擬似乱数しか作ることは出来ません。本の1ページ毎にランダ ...
続きを読む
std::random_device rand_device;
std::mt19937 mersenne(rand_device());
std::uniform_real_distribution<float> rand(0.0f, total);
float randomResult = rand(mersenne);
結果を返却する
上記で生成した乱数から個々の確立を引いていき、0以下になった時点の値を返却します
for (auto per = targetDict.begin(); per != targetDict.end(); per++)
{
randomResult -= per->second;
// 0以下になった時点でのKeyを返す
if (randomResult < 0.0f)
{
return per->first;
}
}
確率でTrue、Falseを返却する
小数点をなくす倍率を求める
小数点をなくすためにべき乗する
// 少数以下の桁数(現在は第1まで)
int decimalPoint = 1;
// 小数点を消すための倍率
float rate = powf(10, decimalPoint);
乱数の上限と判定ボーダーを設定
乱数値と判定ボーダーを比較して判定ボーダーより乱数値が低いかどうか判定を返却します
// 乱数の上限と判定ボーダーを設定
float randomLimit = 100 * rate;
float border = rate * percent;
// 乱数を生成
std::random_device rand_device;
std::mt19937 mersenne(rand_device());
std::uniform_real_distribution<float> rand(0.0f, randomLimit);
float randomResult = rand(mersenne);
// 低いかどうか判定を返却
return randomResult < border;
精度について
上記で紹介した2のそれぞれの制度は以下です
100連を1万回実行した平均になります。それぞれ誤差がありますが許容範囲内だと思います
値 | 確率 | 100連を1万回実行した際の平均値 |
---|---|---|
hoge.png | 20.0 | 20.000 |
huga.png | 25.0 | 25.000 |
piyo.png | 15.0 | 15.000 |
Biyo.png | 40.0 | 39.000 |
True | 20.0 | 19.000 |
False | 80.0 | 80.000 |
最後に
今回紹介したプログラムの全体をGitへアップしています
また、例ではC++しか記載していませんがC#版もアップしています
それぞれ参考にしてください
-
BlogSampleCodeProjects/Probability at main · nasuton/BlogSampleCodeProjects · GitHub
Project for sample code used in the blog.(Blogで記載しているサンプルコード ...
続きを読む