从具有非均匀分布的集合中选择随机元素?

时间:2013-02-16 22:39:55

标签: java data-structures random set

我想有一个集合,并且它的元素具有与它们相关联的概率,所以当我从集合中随机选择一个元素时,分布遵循与元素相关联的概率。我想在一个非常小的Java应用程序中使用它,它存储了我想要观看的电影列表,所以我可以给它建议一个随机电影(我总是需要几个小时才能选择一部电影)。对于每部电影,我想将电影的建议次数与我联系起来,这与从下一个建议的列表中挑选电影的概率成反比。

是否有一种数据结构允许随机选择具有非均匀分布的元素?

如果没有,那么编写这样一个数据结构的最有效方法是什么?我当然总是可以构建一个数组,将列表中的每个元素放入数组中,这样数组中的值分布就可以匹配我希望它们具有的概率,并从该数组中选择一个随机元素。但是对于大型电影来说,这将是非常低效的。我的另一个想法是封装元素和所有元素的概率之和(因此第一个元素将被封装为(第一个,p(第一个)),第二个元素被封装为(第二个,p(第二个)+ p(首先))等等,然后选择一个介于0和1之间的随机数,并在这些封装元素的排序列表上进行二进制搜索。听起来合理吗?

TL:DR(有点抽象):如何在Java中有效地将非均匀分布映射到集合的元素?

3 个答案:

答案 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;
}