Java 可伸缩的随机完整迭代?
假设我有一个列表,如下所示:Java 可伸缩的随机完整迭代?,java,algorithm,collections,random,Java,Algorithm,Collections,Random,假设我有一个列表,如下所示: [a,b,c,d,e] [f,g,h] [i,j,k,l]] 因此,外部列表的大小为3,内部列表的大小为5、3和4 我需要得到这些内部列表中任意一个的随机元素,给每个元素一个相同的随机机会。所以我可以写一个算法: 生成一个介于0和Totalistssize(5+3+4)=12之间的随机数,例如randomIndex 7 迭代所有列表,如果其大小大于其大小,则减去其大小,例如randomIndex 7-firstListSize 5=newRandomIndex
- [a,b,c,d,e]
- [f,g,h]
- [i,j,k,l]]
- 生成一个介于
和0
Totalistssize(5+3+4)=12之间的随机数,例如
randomIndex 7
- 迭代所有列表,如果其大小大于其大小,则减去其大小,例如
randomIndex 7-firstListSize 5=newRandomIndex 2
- 返回下一个列表中的元素,
第二个列表中的随机索引2=元素g
- 不首先初始化所有列表并随机化联接的列表
- 如果持有一个已选择索引的布尔数组,而不必迭代该布尔数组来转换生成的
randomIndex
另一种方法可能是将选定的索引存储在一个集合中,在创建一个新的随机索引后,您可以检查新索引是否已经在“已使用”集合中。然而,如果您想在整个池中选择一个较高的百分比,这种方法将变得越来越慢,因为您会更频繁地使用已使用的索引。如果只从一个大列表中选择几个,这种方法可能会更好,因为它不需要太多的初始化和内存。为什么不生成所有可能索引的排列(换言之,您洗牌序列[0,12])。然后您就知道,您将以随机顺序恰好命中所有元素一次
为了高效查找,您可以保留数组的运行总长度。在您的示例中:0、5、8、12。这样,您可以通过“总索引”进行二进制搜索以查找任何数组。在“弹出”元素时,是否可以从列表中删除这些元素
如果是这样的话,您可以简单地这样做:当您选择一个元素时,只需从列表中删除它,然后在计算下一个索引之前从总大小中减去一个,然后根据需要重复执行。我建议如下:
- 存储整数列表
,以记住每个列表中的选定元素标记
- 然后,要确定哪个元素对应于您的
,请执行以下操作:随机索引
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(); }
List mark=/…每个数组一个标记列表 E[][]列出=/…要从中选择随机元素的列表 void selectAllegementSonce(int totalElementCount){ 随机r=新随机(); 对于(int selected=0;selected
而(mark.get(i).size()>j&&mark.get(i).get(j)则使用以下类: 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; } }
二进制搜索是一个非常好的主意。我仍然需要初始化一个大小为import java.util.Enumeration; 导入java.util.Random; 公共类RandomPermuteIterator实现枚举{ INTC=1013904223,a=1664525; 长种子,N,m,次之; 布尔hasNext=true; 公共随机PermuteIterator(长N)引发异常{ if(N Math.pow(2,62))抛出新异常(“不支持的大小:+N”); 这个,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)); } 公共静态void main(字符串[]args)引发异常{ 随机置换器r=新的随机置换器(100); while(r.hasMoreElements())System.out.print(r.nextElement()+); } @凌驾 公共布尔值hasMoreElements(){ 返回hasNext; } @凌驾 公共长期nextElement(){ 下一步=(a*下一步+c)%m; 而(next>=N)next=(a*next+c)%m; 如果(next==seed)hasNext=false; 下一步返回; } }
的列表并将其洗牌,但我不认为有任何方法可以解决这个问题。当然,你可以在这里用速度换取内存。你希望有多少列表和项目?外部列表的大小约为3到10。内部列表实际上是一个可以轻松生成超过totalistsize
个项目的RATOR。但很常见的是,在随机选择1000
个项目后,算法就会完成。另一方面,它也可以选择所有1000个
个项目:无法预先知道。然后,我为您提供了一个额外的加速:不生成在开始时进行排列。相反,使用哈希表跟踪已使用的索引。但是在选择了5000个项目之后,您可能需要更多的项目。因此,只有这样,您才能生成(剩余索引的)整个排列.这种概率算法节省了生成高概率排列的时间。1000