java—如何只使用每个类的元素数进行采样而不进行替换?

axzmvihb  于 2021-07-06  发布在  Java
关注(0)|答案(4)|浏览(301)

我有一个字符串列表 (["A", "B", ...]) 还有一份尺码表 ([4,7,...]) . 我想样品,而不是从字符串的初始位置字符串集替换 i 出现 sizes[i] 次。我必须做这个手术 k 次。很明显,如果我选绳子 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));
    }
}

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

vd2z7a6w

vd2z7a6w1#

假设包不必是持久性的或完全不用,您可以创建一个包含输入和频率的类,例如(简化):

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

j2qf4p5b2#

假设这两个数组的长度相同,则可以创建一个包含来自这些数组的元素对的Map项列表,并对该列表进行无序排列:

String[] elems = {"A", "B", "C", "D", "E"};
Integer[] sizes = {10, 5, 4, 7, 3};

List<Map.Entry<String, Integer>> bag = IntStream
        .range(0, elems.length)
        .mapToObj(i -> Map.of(elems[i], sizes[i]))
        .flatMap(map -> map.entrySet().stream())
        .collect(Collectors.toList());

System.out.println(bag); // [A=10, B=5, C=4, D=7, E=3]
Collections.shuffle(bag);
System.out.println(bag); // [D=7, C=4, E=3, A=10, B=5]

另请参阅:如果存在重复项,如何根据另一个数组对数组进行排序?

gorkyyrv

gorkyyrv3#

您可以将这两个数组收集到一个Map中:

String[] elems = {"A", "B", "C", "D", "E"};
Integer[] sizes = {10, 5, 4, 7, 3};

Map<String, Integer> map = IntStream.range(0, elems.length).boxed()
        .collect(Collectors.toMap(i -> elems[i], i -> sizes[i]));

System.out.println(map); // {A=10, B=5, C=4, D=7, E=3}
new9mtju

new9mtju4#

如果你只想从 bag ,您不需要洗牌 bag . 你可以用 Random#nextInt(elems.length * sizes.length) 随机抽取 int0elems.length * sizes.length - 1 用这个 int 作为索引,可以从 bag .
演示:

import java.util.ArrayList;
import java.util.Random;

public class Main {
    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]);
            }
        }

        Random random = new Random();
        int count = elems.length * sizes.length;
        for (int i = 0; i < k; i++) {
            System.out.println(bag.get(random.nextInt(count)));
        }
    }
}

相关问题