我想有一个集合,并且它的元素具有与它们相关联的概率,所以当我从集合中随机选择一个元素时,分布遵循与元素相关联的概率。我想在一个非常小的Java应用程序中使用它,它存储了我想要观看的电影列表,所以我可以给它建议一个随机电影(我总是需要几个小时才能选择一部电影)。对于每部电影,我想将电影的建议次数与我联系起来,这与从下一个建议的列表中挑选电影的概率成反比。
是否有一种数据结构允许随机选择具有非均匀分布的元素?
如果没有,那么编写这样一个数据结构的最有效方法是什么?我当然总是可以构建一个数组,将列表中的每个元素放入数组中,这样数组中的值分布就可以匹配我希望它们具有的概率,并从该数组中选择一个随机元素。但是对于大型电影来说,这将是非常低效的。我的另一个想法是封装元素和所有元素的概率之和(因此第一个元素将被封装为(第一个,p(第一个)),第二个元素被封装为(第二个,p(第二个)+ p(首先))等等,然后选择一个介于0和1之间的随机数,并在这些封装元素的排序列表上进行二进制搜索。听起来合理吗?
TL:DR(有点抽象):如何在Java中有效地将非均匀分布映射到集合的元素?
答案 0 :(得分:5)
我不确定我的问题是否正确。我会使用TreeMap<Double, Movie>
。
示例:假设你有电影A(60%),电影B(30%)和电影C(10%)。
TreeMap<Double, Movie> movies = new TreeMap<>();
movies.put(0.6, new Movie("Movie A"));
movies.put(0.9, new Movie("Movie B")); // 0.6 + 0.3
movies.put(1.0, new Movie("Movie C")); // 0.6 + 0.3 + 0.1
Double probability = Math.random(); // between 0 (inclusive) and 1.0 (exclusive)
Movie chosen = movies.higherEntry(probability).getValue();
我将留下人口,并将概率重新排除在你之前。
答案 1 :(得分:3)
有一种很酷的方法称为“别名方法”,可以在O(1)中进行选择。 这里有美化解释:http://pandasthumb.org/archives/2012/08/lab-notes-the-a.html
答案 2 :(得分:2)
我只想定义:
class Movie {
int recommendations;
}
然后做
public Movie chooseMovie(ArrayList<Movie> movies) {
Random rand = new Random();
int sum = 0;
for(Movie movie : movies) {
sum += movie.recommendations;
}
int choice = rand.nextInt(sum);
int soFar = 0;
for(Movie movie : movies) {
soFar += movie.recommendations;
if(choice < soFar) {
return movie;
}
}
return null;
}
如果该范围较大,则选择变量更有可能落入电影的推荐范围内。这很慢,但实际上电影的数量可能足够小,因为它可以为你工作。如果您正在进行大量查找,则可以缓存总推荐金额和增量总和,类似于您建议的方式。
public Movie chooseMovie(ArrayList<Movie> movies) {
Random rand = new Random();
double sum = 0;
for(Movie movie : movies) {
if(movie.recommendations > 0) {
sum += 1 / (double) (movie.recommendations);
}
}
int choice = rand.nextDouble() * sum;
double soFar = 0;
for(Movie movie : movies) {
if(movie.recommendations > 0) {
soFar += 1 / (double) (movie.recommendations);
if(choice < soFar) {
return movie;
}
}
}
return null;
}