可扩展的随机和完整迭代?

时间:2012-01-27 15:49:23

标签: java algorithm collections random

假设我有一个列表列表,如下所示:

  • [[a,b,c,d,e],
  • [f,g,h],
  • [i,j,k,l]]

因此外部列表的大小为3,内部列表的大小为5,3和4。

我需要得到任何内部列表的随机元素,给每个元素一个随机的机会。所以我可以编写一个算法:

  • 0totalListsSize (5 + 3 + 4) = 12之间生成随机广告,例如randomIndex 7
  • 遍历所有列表并减去它们的大小,如果它大于它们的大小,例如randomIndex 7 - firstListSize 5 = newRandomIndex 2
  • 返回下一个列表中的元素randomIndex 2 in secondList = element g

问题在于顺序选择必须完整且可耗尽:在上面的示例中进行了12次连续选择后,我必须选择每个元素一次。

有没有办法可以扩展?

  • 不首先初始化所有列表并随机化加入列表
  • 如果持有已经选择了索引的布尔数组,则无需迭代该布尔数组只是为了翻译生成的randomIndex

5 个答案:

答案 0 :(得分:8)

为什么不生成所有可能索引的排列(换句话说,您将对序列[0,12)进行随机排列)。然后你知道你将按照随机顺序一次性击中所有元素。

为了有效查找,您可以保持数组长度的运行总计。在你的例子中:0,5,8,12。这样你就可以进行二进制搜索,通过“总索引”找到任何数组。

答案 1 :(得分:1)

好吧,您可以创建一组可能的索引,随机选择其中一个索引,删除所选索引并访问相应的对象。

或者,正如您所说,您可以创建一个联合列表并从中选择,删除任何选定的元素。

这两种方法都需要一些初始化,但无论如何你都必须做一些书籍。

另一种方法可能是将所选索引存储在一个集合中,并在创建新的随机索引后,您可以检查新的索引是否已经在“已使用”集合中。但是,如果要选择整个池中较高的百分比,这种方法会变得越来越慢,因为您经常会使用已经使用过的索引。为了从大列表中仅选择一些,这种方法可能会更好,因为它不需要太多的初始化和内存。

答案 2 :(得分:0)

当您“弹出”它们时,是否可以从列表中删除元素?

如果是这样,你可以这样做:只需在选择它时从列表中删除一个元素,然后在计算下一个索引之前从总大小中减去一个元素,然后根据需要重复。

答案 3 :(得分:0)

我建议如下:

  • 存储整数列表mark,列出每个列表中选定的元素
  • 然后,确定哪个元素与您的randomIndex对应:

    List<List<Integer>> mark    = // ... one mark list for each array
    E[][] lists = // ... the lists you want to select random elements from
    
    void selectAllElementsOnce( int totalElementCount ){
        Random r = new Random();
        for(int selected = 0; selected < totalElementCount; selected++){
            E element = this.elementForRandomIndex(r.nextInt(totalElementCount - selected));
            // do something with this element
        }
    }
    
    E elementForRandomIndex( int randomIndex ) {
        for(int i = 0; i < lists.length; i++ ) {
            if(randomIndex < lists[i].length - mark.get( i ).size()) {
                int j = 0;
                while(mark.get( i ).size() > j && mark.get( i ).get( j ) <= randomIndex) {
                    randomIndex++ ;
                    j++ ;
                }
                mark.get( i ).add( j, randomIndex );
                return lists[i][randomIndex];
            } else {
                randomIndex -= lists[i].length - mark.get( i ).size();
            }
        }
        throw new IndexOutOfBoundsException();
    }
    

该解决方案的复杂性在于O(numberOfLists + maximumListSize),用于标记列表的实现,其在恒定时间内提供元素访问(例如,ArrayList)。请注意,它不是两个术语的乘积,因为只迭代了一个列表。

答案 4 :(得分:0)

使用以下课程:

import java.util.Enumeration;
import java.util.Random;

public class RandomPermuteIterator implements Enumeration<Long> {
    int c = 1013904223, a = 1664525;
    long seed, N, m, next;
    boolean hasNext = true;

    public RandomPermuteIterator(long N) throws Exception {
        if (N <= 0 || N > Math.pow(2, 62)) throw new Exception("Unsupported size: " + N);
        this.N = N;
        m = (long) Math.pow(2, Math.ceil(Math.log(N) / Math.log(2)));
        next = seed = new Random().nextInt((int) Math.min(N, Integer.MAX_VALUE));
    }

    public static void main(String[] args) throws Exception {
        RandomPermuteIterator r = new RandomPermuteIterator(100);
        while (r.hasMoreElements()) System.out.print(r.nextElement() + " ");
    }

    @Override
    public boolean hasMoreElements() {
        return hasNext;
    }

    @Override
    public Long nextElement() {
        next = (a * next + c) % m;
        while (next >= N) next = (a * next + c) % m;
        if (next == seed) hasNext = false;
        return  next;
    }
}