首页 > 解决方案 > 如何仅使用每个类的元素数量进行抽样而不进行替换?

问题描述

我有一个字符串(["A", "B", ...])列表和一个尺寸列表([4,7,...])。我想从最初i出现位置字符串的字符串集中进行采样而不进行替换sizes[i]。我必须执行此操作k次数。显然,如果我选择 string i,则sizes[i]减 1。我开发的当前简单的解决方案是生成整个输入集,将其打乱,然后迭代地弹出数组的第一个元素。这显然是低效的,因为如果一个字符串出现 100 万次,我将不得不生成 100 万个条目。

public static void main(String[] args) {
    String[] elems = { "A", "B", "C", "D", "E" };
    Integer[] sizes = { 10, 5, 4, 7, 3 };
    int k = 3;

    ArrayList<String> bag = new ArrayList<>();
    for (int i = 0; i < elems.length; i++) {
        for (int j = 0; j < sizes[i]; j++) {
            bag.add(elems[i]);
        }
    }

    Collections.shuffle(bag);
    for (int i = 0; i < k; i++) {
        System.out.println(bag.remove(0));
    }
}

有没有更好更有效的方法来执行这个操作?谢谢。

标签: javaarrayssortingprobability

解决方案


假设袋子不必是持久的或根本不需要使用,您可以创建一个包含输入和频率的类,例如像这样(简化):

class SampleElement<T> {
  private T value;
  private int frequency;

  //constructors, getters, setters
}

然后从您拥有的输入中构建这些元素的集合,例如(再次简化):

 List<SampleElement<String>> samples = Arrays.asList(new SampleElement<String>("A",10), ...);

最后循环直到该集合为空,或者您已经完成了k多次并选择一个随机元素。降低该元素的频率,如果它达到 0,则将其从集合中删除。示例(在我的脑海中,因此可能包含错误):

Random rand = new Random();
int runs = k;
while(runs > 0 && !samples.isEmpty() ) {
  runs--;
  int index = rand.nextInt(samples.size());
  SampleElement<String> element = samples.get(index);

  System.out.println(element.getValue());

  element.decrementFrequency();
  if( element.getFrequency() <= 0 ) {
    samples.remove(index);
  }
}

推荐阅读