Edited at
Siv3DDay 1

抽選するプログラム (C++/OpenSiv3D)

ゲームやアプリで 抽選ランダムな選択 を行うことがあります。選ばれる確率が一定 or 異なる、一度に 1 つを選ぶ or 複数を選ぶなど、様々なケースに応じた C++/OpenSiv3D プログラムの書き方を紹介します。


ある確率でイベントが起こる


30% の確率で当たりくじを引く

RandomBool(p) 関数は、引数に渡された確率 p にしたがって、ランダムに truefalse を返します。

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());
}


出力例.txt

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());
}


出力例.txt

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());
}


出力例.txt

京都

福岡
東京
京都
大阪
横浜
仙台
仙台
大阪
大阪


集合から複数の要素をランダムに取り出す(均等な確率)


ビンゴカードを作る



サイコロや目的地のケースと異なり、一度使用した番号を使わない 制約があります。

このようなケースでは 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());
}


出力例.txt

{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());
}


出力例.txt

{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());
}


出力例.txt

★★★

★★





★★


用途に合わせた関数を使って、簡潔で高速な抽選システムを実装しましょう。