如果您希望提高运行时选择效率,那么在设置上多花一点时间可能是最好的选择。这是一种可能的解决方案。它有更多代码,但保证 log(n) 选择。
WeightedItemSelector 实现从加权对象集合中选择随机对象。
选择保证在 log(n) 时间内运行。
public class WeightedItemSelector<T> {
private final Random rnd = new Random();
private final TreeMap<Object, Range<T>> ranges = new TreeMap<Object, Range<T>>();
private int rangeSize; // Lowest integer higher than the top of the highest range.
public WeightedItemSelector(List<WeightedItem<T>> weightedItems) {
int bottom = 0; // Increments by size of non zero range added as ranges grows.
for(WeightedItem<T> wi : weightedItems) {
int weight = wi.getWeight();
if(weight > 0) {
int top = bottom + weight - 1;
Range<T> r = new Range<T>(bottom, top, wi);
if(ranges.containsKey(r)) {
Range<T> other = ranges.get(r);
throw new IllegalArgumentException(String.format("Range %s conflicts with range %s", r, other));
}
ranges.put(r, r);
bottom = top + 1;
}
}
rangeSize = bottom;
}
public WeightedItem<T> select() {
Integer key = rnd.nextInt(rangeSize);
Range<T> r = ranges.get(key);
if(r == null)
return null;
return r.weightedItem;
}
}
Range 实现范围选择以利用 TreeMap 选择。
class Range<T> implements Comparable<Object>{
final int bottom;
final int top;
final WeightedItem<T> weightedItem;
public Range(int bottom, int top, WeightedItem<T> wi) {
this.bottom = bottom;
this.top = top;
this.weightedItem = wi;
}
public WeightedItem<T> getWeightedItem() {
return weightedItem;
}
@Override
public int compareTo(Object arg0) {
if(arg0 instanceof Range<?>) {
Range<?> other = (Range<?>) arg0;
if(this.bottom > other.top)
return 1;
if(this.top < other.bottom)
return -1;
return 0; // overlapping ranges are considered equal.
} else if (arg0 instanceof Integer) {
Integer other = (Integer) arg0;
if(this.bottom > other.intValue())
return 1;
if(this.top < other.intValue())
return -1;
return 0;
}
throw new IllegalArgumentException(String.format("Cannot compare Range objects to %s objects.", arg0.getClass().getName()));
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{\"_class\": Range {\"bottom\":\"").append(bottom).append("\", \"top\":\"").append(top)
.append("\", \"weightedItem\":\"").append(weightedItem).append("}");
return builder.toString();
}
}
WeightedItem 只是封装了要选择的项目。
public class WeightedItem<T>{
private final int weight;
private final T item;
public WeightedItem(int weight, T item) {
this.item = item;
this.weight = weight;
}
public T getItem() {
return item;
}
public int getWeight() {
return weight;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{\"_class\": WeightedItem {\"weight\":\"").append(weight).append("\", \"item\":\"")
.append(item).append("}");
return builder.toString();
}
}