我需要从列表中选择一个满足特定条件的随机元素。我一直在使用的方法有效,但我确信并非一切都有效。最有效的方法是什么?
以下代码位于while(true)循环内,因此在每次迭代时对列表进行洗牌显然效率不高。
Foo randomPick = null;
Collections.shuffle(myList);
for (Foo f : myList) {
if (f.property) {
randomPick = f;
break;
}
}
提前致谢!
答案 0 :(得分:7)
最有效的解决方案部分取决于您选择随机元素的频率,是否要选择不同的随机元素,以及哪些元素符合标准
一些选择:
ArrayList
的常见情况,从列表中间删除也是O(n)操作:( (请注意,这主要是假设ArrayList
的复杂性。例如,LinkedList
中的特定索引是O(n),但是删除很便宜。)
答案 1 :(得分:3)
使用lazy shuffle:它只在需要时计算并产生混洗元素。
C#实现,但很容易转换为Java:Is using Random and OrderBy a good shuffle algorithm?
答案 2 :(得分:2)
为什么不选择一个随机索引整数,然后测试它的属性呢?
而不是改组
public Foo picker() {
while (true) { // TODO: realistically, you need to deal with exit conditions
int randIdx = myRand.nextInt();
Foo randomPick = myList.get(randIdx % randIdx);
if (randomPick.property)
return randomPick;
}
}
注意这确实假设列表的非平凡数字的属性为true,并且这些属性会发生变化。
如果两个假设被伪造,你需要选择一个子集,然后随机选择其中一个。
答案 3 :(得分:1)
由于你正在进行洗牌,你基本上只触及每个元素一次,所以你已经有一个至少为N的大O.如果你选择一个随机索引,然后测试该位置的元素,那么你是在你触及N个元素之前得到你想要的变量,保证,从而保证改进。如果你有20%的元素分布,那么你会期望每5个随机索引给你一个符合你标准的元素。虽然这不是保证,但这是一个可能性。在绝对最坏的情况下,你会选择所有80%不符合你标准的元素,然后下一个将是你的随机元素。你的最大执行将被限制为.8N + 1,仍然优于N.平均而言,你的大O成本将是5-10的常数。随着N的增加,WAAAAY在执行方面更好。
答案 4 :(得分:1)
只需选择一个随机索引;
Random r = new Random();
while (true)
{
...
Foo element = null;
while (element == null)
{
Foo f = myList.get(r.nextInt(myList.size());
if (f.property) element = f;
}
...
}
答案 5 :(得分:0)
我建议从myList
中制作一个临时过滤列表,其中每个项目都符合您的条件。然后在每次迭代时,您可以随机播放并选择顶部项目,或使用随机索引选择随机项目。
答案 6 :(得分:0)
List<Foo> matchingItems = new ArrayList<Foo>();
for(Foo f : myList) {
if (f.property) matchingItems.add(f);
}
Random randomGenerator = new Random();
int index = randomGenerator.nextInt(matchingItems.size());
Foo randomFoo = matchingItems.get(index);
答案 7 :(得分:0)
您可以使用linear congruential random number generator循环遍历列表的索引,再加上一些可以选择合适的参数,然后检查此序列索引的值,同时丢弃索引大,挑选你找到的第一个元素。如果你发现什么都没有,你可以在随机序列开始重复时停止。
为此, m 需要至少与列表一样大。为了能够选择 a ,模数 m 必须包含因子8或某个素数的平方&gt; = 3.c应该随机选择,以便它是相对的素数到m。也可以随机选择初始值。
答案 8 :(得分:0)
不止一次随机地改组阵列是愚蠢的。
洗牌一次。然后,对于随机值的每个请求,按顺序返回下一个值,直到您的列表用完为止。
答案 9 :(得分:0)
O(N):
Random r = new Random();
int seen = 0;
Foo randomPick = null;
for (Foo foo : myList) {
if (foo.property && r.nextInt(++seen) == 0) {
randomPick = foo;
}
}
答案 10 :(得分:0)
请查看此链接
http://www.javamex.com/tutorials/random_numbers/random_sample.shtml
public static <T> T[] pickSample(T[] population, int nSamplesNeeded, Random r) {
T[] ret = (T[]) Array.newInstance(population.getClass().getComponentType(),
nSamplesNeeded);
int nPicked = 0, i = 0, nLeft = population.length;
while (nSamplesNeeded > 0) {
int rand = r.nextInt(nLeft);
if (rand < nSamplesNeeded) {
ret[nPicked++] = population[i];
nSamplesNeeded--;
}
nLeft--;
i++;
}
return ret;
}