抱歉耽搁了,但我认为我有一个相对优雅的解决方案,包括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)类使用 ajava.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)
时间。