O(n lg n)
遅れて申し訳ありませんが、構築時間とO(lg n)
ランダム要素のフェッチ時間で比較的エレガントなソリューションがあると思います。ここに行きます。
WeightedProbMap:
このクラスは、ランダム要素ジェネレーターを実装します。に基づいて構築されIterable
ます。以下を参照してくださいTest.java
。
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
class WeightedProbMap<EltType> {
private SortedMap<Integer, EltType> elts = new TreeMap<Integer, EltType>();
private Random rand = new Random();
private int sum = 0;
// assume: each weight is > 0; there is at least one element;
// elements should not be repeated
// ensure: this.elts maps cumulative weights to elements;
// this.sum is the total weight
public WeightedProbMap(Iterable<Pair<Integer, EltType>> weights) {
for (Pair<Integer, EltType> e : weights) {
this.elts.put(this.sum, e.second);
this.sum += e.first;
}
}
// assume: this was initialized properly (cf. constructor req)
// ensure: return an EltType with relative probability proportional
// to its associated weight
public EltType nextElt() {
int index = this.rand.nextInt(this.sum) + 1;
SortedMap<Integer, EltType> view = this.elts.headMap(index);
return view.get(view.lastKey());
}
}
Pair.java:シンプルな Pair クラスです。
class Pair<X, Y> {
public Pair(X x, Y y) {
first = x;
second = y;
}
X first;
Y second;
}
Test.java:WeightedProbMap
(WPM) クラス用の非常に単純なテスト ハーネスです。関連する重みを持つ要素の ArrayList を構築し、それを使用して WPM を構築し、WPM から 10,000 サンプルを取得して、要素が期待される頻度で表示されるかどうかを確認します。
import java.util.ArrayList;
class Test {
public static void main(String argc[]) {
ArrayList<Pair<Integer, String> > elts = new ArrayList<Pair<Integer, String>>();
elts.add(new Pair<Integer, String>(20, "Hello"));
// elts.add(new Pair<Integer, String>(70, "World"));
// elts.add(new Pair<Integer, String>(10, "Ohai"));
WeightedProbMap<String> wpm = new WeightedProbMap<String>(elts);
for (int i = 0; i < 10000; ++i) {
System.out.println(wpm.nextElt());
}
}
}
これをテストする:
elts.add(...)
のいずれかまたは両方の行のコメントを外しTest.java
ます。
コンパイル:
$ javac Pair.java WeightedProbMap.java Test.java
次のように実行します (たとえば、Unix の場合):
$ java Test | grep "Hello" | wc -l
これにより、その特定の実行のカウントが得られます。
説明:
コンストラクター:
( WeightedProbMap
WPM) クラスは、aを使用しjava.util.SortedMap
て累積重みを要素にマップします。グラフィカルな説明:
The constructor takes weights... ...and creates a mapping from the
3 +---+ number line:
| |
2 +---+ +---+ 2 0 2 5 7
| | | | +------+---------+------+
| | | | | X | Y | Z |
--+---+---+---+-- +------+---------+------+
X Y Z
nextElt()
:
ASortedMap
はデータをキーの順序で格納します。これにより、マップのサブセットの「ビュー」を低コストで提供できます。特に、ライン
SortedMap<Integer, EltType> view = this.elts.headMap(index)
this.elts
は、厳密に より小さいキーのみを持つ元のマップ ( ) のビューを返しますindex
。この操作 ( headMap
) は一定時間です。構築に時間view
がかかり、後で変更した場合、変更も に反映されます。O(1)
this.elts
view
view
乱数以外のすべての を作成したら、そのサブセットで最大のキーを見つける必要があります。でそれを行いますがSortedMap.lastKey()
、 のTreeMap
場合は時間がかかり\Theta(lg n)
ます。