挑选从满足某些条件的列表中随机元素的最快方法(Fastest way to pick a rando

2019-10-18 08:42发布

我需要从一个列表,即满足一定的条件选择一个随机元素。 这种方法我一直使用的作品,但我敢肯定,是不是每一个高效。 什么是最有效的方式做到这一点?

下面的代码是一个while(true)循环里面,这样显然不是非常有效的洗牌在每个迭代上的列表中。

Foo randomPick = null;
Collections.shuffle(myList);
for (Foo f : myList) {
    if (f.property) {
        randomPick = f;
        break;
    }
}

提前致谢!

Answer 1:

最有效的解决方案将部分取决于你要多久挑选随机元素,你是否要选择不同的随机元素,以及多大比例的元素符合标准

有几个选项:

  • 创建只包含符合标准的元素的副本。 然后,您可以混洗,并遍历它连续不同随机元素,或者只是通过拾取随机指数挑选任意的随机元素。 这显然是O(n)的设置在时间和空间,但高效其后。
  • 洗牌收集一次,然后遍历你正在做的 - 但保留迭代器。 或者,使用索引手动执行迭代。 这将让你得到不同的随机元素。 这又是O(n)的设置。
  • 请选择从原来的列表中随机元素,直到找到一个符合标准。 这样做,如果你有一个大名单,只有少数“有效”的项目,虽然可能需要花费很长的时间。 这不需要设置,但你可能最终会通过反复测试相同的元素“浪费”的工作。
  • 混合方法:保持采摘从原来的列表中随机元素,但除去他们,如果他们不符合标准。 不幸的是从列表的中间去除是O(n)的操作也为一般情况下ArrayList :(

(请注意,这我已经大多假设ArrayList复杂性。获得一个特定的指数在LinkedList是O(n),例如,但随后去除很便宜。)



Answer 2:

Use a lazy shuffle: it calculates and yields the shuffled elements only as they are needed.

A C# implementation, but easily converted to Java: Is using Random and OrderBy a good shuffle algorithm?



Answer 3:

相反洗牌,为什么不选择一个随机指数整数,然后测试其性能?


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

请注意 ,这确实认为这个列表的一个不平凡的数量确实有真正的财产,而这些特性发生变化。

如果两个假设将被证伪,你要选择一个子集,然后随机选择其中的一个。



Answer 4:

既然你正在做一个洗牌,你基本上接触的每个元素至少一次,所以你已经有至少N的一大Ø如果你选择一个随机指数,然后在该位置测试元素,那么你会得到你想你已经触及N个元素之前的变量,有保障,从而保证改善。 如果你有元素的20%分布,那么你会希望每5随机指标给你满足您的条件的元素。 虽然这不是一个保证,这是一个probabliltiy。 绝对最坏的情况下,你会选择未满足条件的元素的所有80%,那么下一个会是你的随机元素。 你最大的执行将仅限于将将被.8N + 1,仍优于N.,平均你的大O开销会像5-10的常数。 在执行作为N增加的条款WAAAAY更好。



Answer 5:

只要选择一个随机指数;

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;
    }
    ...
}


Answer 6:

我建议做一个临时的过滤列表出来的myList ,每一个项目满足您的条件。 然后在每次迭代,你可以将它洗,并挑选了顶部的项目,或者使用随机指数选择一个随机的项目。



Answer 7:

你可以使用一个线性同余随机数发生器 ,其循环一次通过列表的指标,再加上可能多了一些能够选择合适的参数,然后检查由该序列索引的值,丢弃它太大了,并且采摘指标你找到的第一个元素。 如果你发现没有,当随机顺序开始重复,你可以停下来。

对于这个工作需要的至少为列表一样大。 为了能够选择的模数m必须包含8倍或一些素> = 3℃的正方形应该被随机地选择,以便它是互质为m。 还搭载初始值随机。



Answer 8:

随机洗牌数组不止一次是愚蠢的。

随机播放一次。 那么,对于一个随机值的每个请求,为了返回一个值,直到你耗尽你的清单。



Answer 9:

为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;
  }
}


Answer 10:

    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);


Answer 11:

请检查该链接

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


文章来源: Fastest way to pick a random element from a list that fulfills certain condition