web-dev-qa-db-ja.com

確率でアイテムを選ぶ方法は?

アイテムのリストがあります。これらの各アイテムには、独自の確率があります。

誰かがその確率に基づいてアイテムを選択するアルゴリズムを提案できますか?

60
Ruzanna

したがって、各アイテムには相対的な確率を示す数値が格納されます。たとえば、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));
    }
}
33
Usman Ismail
  1. 均一に分布した乱数を生成します。
  2. 訪問した要素の累積確率が乱数よりも大きくなるまでリストを繰り返し処理します

サンプルコード:

double p = Math.random();
double cumulativeProbability = 0.0;
for (Item item : items) {
    cumulativeProbability += item.probability();
    if (p <= cumulativeProbability) {
        return item;
    }
}
73
Brent Worden

私たちは次のリストを持っているふりをします

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

27
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(両端を含む)の間のランダムな整数を生成すると、上記のような決定を下すことができます。

12
0605002

shman'sBrent'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である必要はありません。自動的に正規化されます。

11
Dmitry

私の方法はとても簡単です。乱数を生成します。これで、アイテムの確率がわかっているので、ソートされた確率のリストを繰り返し処理し、ランダムに生成された数よりも確率が低いアイテムを選択します。

詳細については、私の答えをお読みください こちら

5
HackCode

遅いが簡単な方法は、すべてのメンバーにその確率に基づいて乱数を選択させ、最も高い値を持つものを選択させることです。

アナロジー:

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である必要はありません。

5
WVrock

ブレントの答え は良いですが、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;
    }
}
1
Dallas Edwards

コードにサードパーティの依存関係を追加してもかまわない場合は、 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();

免責事項:私は図書館の著者であるため、推奨するときに偏見があるかもしれません。

0
Andrei Ciobanu