アイテムのリストがあります。これらの各アイテムには、独自の確率があります。
誰かがその確率に基づいてアイテムを選択するアルゴリズムを提案できますか?
したがって、各アイテムには相対的な確率を示す数値が格納されます。たとえば、3つのアイテムがある場合、他の2つのアイテムのいずれかが選択される可能性が2倍になると、リストは次のようになります。
[{A,1},{B,1},{C,2}]
次に、リストの番号を合計します(この例では4)。次に、乱数を生成し、そのインデックスを選択します。 int index = Rand.nextInt(4);インデックスが正しい範囲にあるような数を返します。
Javaコード:
class Item {
int relativeProb;
String name;
//Getters Setters and Constructor
}
...
class RandomSelector {
List<Item> items = new List();
Random Rand = new Random();
int totalSum = 0;
RandomSelector() {
for(Item item : items) {
totalSum = totalSum + item.relativeProb;
}
}
public Item getRandom() {
int index = Rand.nextInt(totalSum);
int sum = 0;
int i=0;
while(sum < index ) {
sum = sum + items.get(i++).relativeProb;
}
return items.get(Math.max(0,i-1));
}
}
サンプルコード:
double p = Math.random();
double cumulativeProbability = 0.0;
for (Item item : items) {
cumulativeProbability += item.probability();
if (p <= cumulativeProbability) {
return item;
}
}
私たちは次のリストを持っているふりをします
Item A 25%
Item B 15%
Item C 35%
Item D 5%
Item E 20%
すべての確率が整数であるふりをして、各項目に次のように計算される「範囲」を割り当てましょう。
Start - Sum of probability of all items before
End - Start + own probability
新しい番号は次のとおりです
Item A 0 to 25
Item B 26 to 40
Item C 41 to 75
Item D 76 to 80
Item E 81 to 100
ここで、0から100までの乱数を選択します。32を選択するとします。32はアイテムBの範囲に含まれます。
mj
ルーレットホイールの選択 を試すことができます。
最初にすべての確率を追加し、次にそれぞれを合計で除算して、すべての確率を1のスケールでスケーリングします。確率がA(0.4), B(0.3), C(0.25) and D(0.05)
であるとします。次に、範囲[0、1]のランダムな浮動小数点数を生成できます。これで、次のように決定できます。
random number between 0.00 and 0.40 -> pick A
between 0.40 and 0.70 -> pick B
between 0.70 and 0.95 -> pick C
between 0.95 and 1.00 -> pick D
また、ランダムな整数を使用して行うこともできます-0から99(両端を含む)の間のランダムな整数を生成すると、上記のような決定を下すことができます。
shman's 、 Brent's および@kaushayaの回答で説明されているアルゴリズムは、 Apache commons-math ライブラリに実装されています。
EnumeratedDistribution classを見てください(groovyコードが続きます):
def probabilities = [
new Pair<String, Double>("one", 25),
new Pair<String, Double>("two", 30),
new Pair<String, Double>("three", 45)]
def distribution = new EnumeratedDistribution<String>(probabilities)
println distribution.sample() // here you get one of your values
確率の合計は1または100である必要はありません。自動的に正規化されます。
私の方法はとても簡単です。乱数を生成します。これで、アイテムの確率がわかっているので、ソートされた確率のリストを繰り返し処理し、ランダムに生成された数よりも確率が低いアイテムを選択します。
詳細については、私の答えをお読みください こちら 。
遅いが簡単な方法は、すべてのメンバーにその確率に基づいて乱数を選択させ、最も高い値を持つものを選択させることです。
アナロジー:
3人のうち1人を選択する必要があるが、確率はそれぞれ異なると想像してください。顔の数を変えて死にます。最初の人のサイコロには4つの顔、2人目の人の6、3人目の人の8のサイコロがあります。
次のリストがあるとしましょう:
[{A,50},{B,100},{C,200}]
擬似コード:
A.value = random(0 to 50);
B.value = random(0 to 100);
C.value = random (0 to 200);
最も価値の高いものを選択します。
上記のこの方法は、確率を正確にマップしません。たとえば、100は50の2倍のチャンスはありません。しかし、メソッドを少し調整することでそれを行うことができます。
方法2
0から重みまでの数字を選択する代わりに、前の変数の上限から現在の変数の追加まで制限できます。
[{A,50},{B,100},{C,200}]
擬似コード:
A.lowLimit= 0; A.topLimit=50;
B.lowLimit= A.topLimit+1; B.topLimit= B.lowLimit+100
C.lowLimit= B.topLimit+1; C.topLimit= C.lowLimit+200
結果の制限
A.limits = 0,50
B.limits = 51,151
C.limits = 152,352
次に、0から352までの乱数を選択し、それを各変数の制限と比較して、乱数が制限内にあるかどうかを確認します。
ランダム生成は1つしかないため、このTweakのパフォーマンスは向上すると考えています。
他の回答にも同様の方法がありますが、この方法では合計が100または1.00である必要はありません。
ブレントの答え は良いですが、p = 0の場合に確率が0のアイテムを誤って選択する可能性を考慮していません。そもそもアイテムを追加しない):
double p = Math.random();
double cumulativeProbability = 0.0;
for (Item item : items) {
cumulativeProbability += item.probability();
if (p <= cumulativeProbability && item.probability() != 0) {
return item;
}
}
コードにサードパーティの依存関係を追加してもかまわない場合は、 MockNeat.probabilities() メソッドを使用できます。
例えば:
String s = mockNeat.probabilites(String.class)
.add(0.1, "A") // 10% chance to pick A
.add(0.2, "B") // 20% chance to pick B
.add(0.5, "C") // 50% chance to pick C
.add(0.2, "D") // 20% chance to pick D
.val();
免責事項:私は図書館の著者であるため、推奨するときに偏見があるかもしれません。