ゲームやアプリで 抽選 や ランダムな選択 を行うことがあります。選ばれる確率が一定 or 異なる、一度に 1 つを選ぶ or 複数を選ぶなど、様々なケースに応じた C++/OpenSiv3D プログラムの書き方を紹介します。
ある確率でイベントが起こる
30% の確率で当たりくじを引く
RandomBool(p)
関数は、引数に渡された確率 p
にしたがって、ランダムに true
か false
を返します。
30% の確率で true
を得るには Random(0.3)
です。
# include <Siv3D.hpp>
void Main()
{
for (int32 i = 0; i < 10; ++i)
{
// 30 % の確率で true, 70% の確率で false を返す
Print << RandomBool(0.3);
}
while (System::Update());
}
false
false
false
false
false
true
false
true
false
true
複数の選択肢からランダムに 1 つ選ぶ(均等な確率)
サイコロをふる
同様に確からしい確率で、ある範囲に含まれる整数をランダムに選ぶ場合は Random(min, max)
関数を使います。
1~6 が書かれたサイコロの結果を得るには Random(1, 6)
です。
# include <Siv3D.hpp>
void Main()
{
for (int32 i = 0; i < 10; ++i)
{
// 1 以上 6 以下の数をランダムに返す
Print << Random(1, 6);
}
while (System::Update());
}
6
4
3
5
6
1
5
4
1
4
集合からランダムに 1 つ選択する(均等な確率)
ランダムに目的地を決める
用意してある配列データから 1 つの値を選ぶときは Array::choice()
が便利です。
# include <Siv3D.hpp>
void Main()
{
const Array<String> cities =
{
U"札幌", U"仙台", U"東京", U"横浜", U"名古屋",
U"京都", U"大阪", U"神戸", U"広島", U"福岡"
};
for (int32 i = 0; i < 10; ++i)
{
// 配列の要素からランダムに 1 つ選択
Print << cities.choice();
}
while (System::Update());
}
京都
福岡
東京
京都
大阪
横浜
仙台
仙台
大阪
大阪
集合から複数の要素をランダムに取り出す(均等な確率)
ビンゴカードを作る
サイコロや目的地のケースと異なり、一度使用した番号を使わない 制約があります。
このようなケースでは 1~75 の全ての数を配列に格納して Array::choice(n)
を使うことで、重複しないように n
個の要素を選択できます。
結果の配列は、当初の順序を保って値が小さい順に並んでいるので、Array::shuffle()
で順番をシャッフルするのを忘れないようにしましょう。
# include <Siv3D.hpp>
void Main()
{
// { 1, 2, 3, ..., 75 } の配列を作成
const Array<int32> numbers = Range(1, 75);
// 重複を許さず 25 個の要素を選択
Array<int32> results = numbers.choice(25);
// この時点で数の順序は変わっていない(小さい順)
Print << results;
// 順序をランダムにするためにシャッフル
results.shuffle();
Print << results;
while (System::Update());
}
{1, 3, 16, 18, 30, 31, 34, 35, 39, 42, 44, 46, 47, 49, 52, 53, 57, 60, 61, 63, 67, 68, 69, 72, 74}
{35, 61, 57, 18, 53, 69, 60, 30, 72, 63, 52, 68, 39, 46, 44, 49, 16, 1, 74, 42, 67, 31, 3, 34, 47}
uint32 型のランダムな ID を 100 個生成する
ビンゴカードのケースと異なり、uint32
型の ID の選択肢は 40 億個以上 あるため、配列を作成して Array::choice(n)
することはできません。
このようなケースでは、ランダムな値を生成するごとに、HashSet を使ってこれまでの ID と重複がないかをチェックします。
HasSet::insert
は、要素を追加するとき、その値がすでに存在していたら、戻り値の second
部分が false
になります。
# include <Siv3D.hpp>
void Main()
{
Array<uint32> idList;
for(HashSet<uint32> reserved; idList.size() < 100;)
{
// ランダムな uint32 型の値を生成
const uint32 value = Random<uint32>(Largest<uint32>());
if (!reserved.insert(value).second)
{
// すでに同じ値がある場合はやりなおし
continue;
}
idList << value;
}
Print << idList;
while (System::Update());
}
{3229347734, 1953432812, 550965512, 1371021958, 1375041536, 3487488955, 3627240948, 1691294923, 566624821, 26500009, 879028738, 1592832790, 4157735074, 2387705014, 3139796959, 2655139832, 3485895190, 3338896242, 294058348, 337388880, 8780761, 3203784010, 3044718502, 790169906, 2808952376, 4086756990, 2172472708, 2881672836, 63719007, 2178763472, 681461789, 2859872541, 1732324329, 1205894110, 1990285747, 2849400797, 2881360344, 1066258756, 1726436106, 2895113654, 1665898651, 3132533310, 1090053983, 2651873845, 3681606511, 1083403134, 212008756, 1913963110, 2751918639, 1769198644, 317470050, 1000279371, 4016423153, 420798583, 2749822270, 22767687, 1408002915, 1889722540, 2854369142, 2244555779, 18554211, 3573335033, 1399786361, 3647524309, 3643605667, 86764166, 622672486, 2012885757, 2424136685, 1004656069, 123439045, 4213705520, 4075920828, 519017689, 3120796712, 823653176, 665862823, 3249708005, 2803952774, 3750178571, 1010630758, 1074553705, 449881868, 2900021154, 3647712880, 2797866448, 1690735920, 188786311, 3036402802, 1842889409, 496441267, 3088651060, 3669787076, 3749964443, 3017167199, 1038745033, 3857463142, 3557317987, 3294000666, 2878195577}
複数の選択肢(それぞれ異なる出現確率)からランダムに選択する
出現確率を制御する
ある選択肢は 30% の確率で出現、ある選択肢は 0.1% の確率で出現といったように、選択肢ごとに出現確率を制御したい場合は DiscreteDistribution
を使います。
DiscreteDistribution
にそれぞれの選択肢の選ばれやすさを指定し、選択肢とともに DiscreteSample
に渡すと、その確率に応じた結果が返されます。
DiscreteDistribution
で指定する値の合計は、ちょうど 1.0 や 100 である必要はありません。
# include <Siv3D.hpp>
void Main()
{
// 選択肢
const Array<String> items
{
U"★",
U"★★",
U"★★★",
U"★★★★",
U"★★★★★"
};
// それぞれの選択肢の「選ばれやすさ」
// ★ は ★★★★★ の 100 倍出現しやすい
const DiscreteDistribution<size_t> weights
{
100.0,
50.0,
10.0,
4.0,
1.0
};
for (int32 i = 0; i < 10; ++i)
{
Print << DiscreteSample(items, weights);
}
while (System::Update());
}
★★★
★★
★
★
★
★
★
★★
★
用途に合わせた関数を使って、簡潔で高速な抽選システムを実装しましょう。