我必须根据对象的权重/概率从列表中检索随机对象。我已经找到了解决问题的不同方法,但我想分享另一种方法来了解它是否是一个好方法或者如何改进它。
我们应该假设对象的权重/概率是0到1之间的浮点值。
首先,列表中的对象应实现"加权"接口:
public interface Weighted {
public float getWeight();
}
然后我们应该以这种方式扩展ArrayList:
public class RandomWeightedList<T extends Weighted> extends ArrayList<T>{
public T extractRandomWeightedObject(){
//We sum all the weights
float weightSum = 0F;
for(int i=0;i<this.size();i++){
weightSum += this.get(i).getWeight();
}
//We generate a random float between 0 and weight sum
float random = (new Random()).nextFloat() * weightSum;
//We start range limits
float lowerRangeLimit = 0;
float upperRangeLimit = 0;
//We iterate the list calculating the upper range limit and we check
// wether the random number is lower than the upper limit. If it is,
// we save the instance and break;
for(int i=0;i<this.size();i++){
upperRangeLimit = lowerRangeLimit + this.get(i).getWeight();
if(random < upperRangeLimit){
return this.get(i);
}
lowerRangeLimit = upperRangeLimit;
}
throw new RuntimeException("Should never reach here...");
}
}
最后我们会像这样使用它:
RandomWeightedList<Banner> bannerList = new RandomWeightedList<Banner>();
//Code to fill the list with weighted objects
//...
Banner randomBanner = bannerList.extractRandomWeightedObject();
我如何改进随机抽取算法?
扩展ArrayList是一个很好的解决方案吗?还有其他吗?
提前致谢
我通过一个具有较低内存消耗和较高性能的大型列表解决方案来改变随机对象提取算法。在这种情况下,无需参数化精度。
我对List和界面的命名有语义上的疑问。是否有任何关于命名引用具有概率字段的对象的接口的建议?对于可以根据概率提取随机对象的列表?
答案 0 :(得分:0)
首先,我建议不要继承ArrayList。这样做意味着您的类是一个ArrayList,而ArrayList可以执行的任何操作都是合法的。事实并非如此。你应该拥有一个ArrayList而不是声称是一个。
我还建议添加一个构造函数来设置一次权重。每次要进行extractRandomWeightedObject()
操作时,您都不应该重新计算所有内容。
最后,我实际建议使用别名表来有效地生成具有不同概率(权重)的对象。在初始设置(O(k)
用于具有k
结果的分布)之后,别名表将在每次调用的恒定时间内以及正确的概率产生结果。您可以从github下载Java实现。该实现采用概率/比例而不是权重作为构造函数的输入,但您可以通过将每个对象的权重除以所有对象的总权重,将权重方案转换为概率。