在 ArrayList 中查找最常见的随机分配字符串

Find most common randomly assigned string in ArrayList

我正在开发一个模拟器,其中 Person 对象(存储在 ArrayList 中)"reproduce" 并制造婴儿,它们继承 "genes",表示为 4 个字母的字符串。在程序开始时,第一个人的基因库是随机生成的。

在计时器的每个滴答声中,我想计算所有 Person 对象中最常见的 "gene" 是什么。

这四个字母是:
1. G、Z、N、F
2. A、T、C、G
3. B、F、Q、N
4. A、C、T、E

在这种情况下有 256 种可能的组合,必须有比 256 种 if-else 语句更有效的检查。

人物 class(减去 get/set 方法)

public class Person {
    static Random rand = new Random();
    private Person mother;
    private Person father;
    private String genes;
    private char sex;
    private int age, numKids;

    public Person() {
        mother = null;
        father = null;
        genes = createGenes();
        if (rand.nextDouble() <= 0.5)
            sex = 'm';
        else
            sex = 'f';
        age = 18;
        numKids = 0;
    }

    public Person(Person m, Person f) {
        mother = m;
        father = f;
        genes = inheritGenes(m, f);
        if (rand.nextDouble() <= 0.5)
            sex = 'm';
        else
            sex = 'f';
        age = 0;
    }
//create genes for original Persons
    private String createGenes() {
        String genetics = "";

        double first = rand.nextDouble();
        double second = rand.nextDouble();
        double third = rand.nextDouble();
        double fourth = rand.nextDouble();

        if (first <= 0.25)
            genetics += "G";
        else if (first <= 0.68)
            genetics += "Z";
        else if (first <= 0.9)
            genetics += "N";
        else
            genetics += "F";

        if (second <= 0.65)
            genetics += "A";
        else if (second <= 0.79)
            genetics += "T";
        else if (second <= 0.85)
            genetics += "C";
        else
            genetics += "G";

        if (third <= 0.64)
            genetics += "B";
        else if (third <= 0.95)
            genetics += "F";
        else if (third <= 0.98)
            genetics += "Q";
        else
            genetics += "N";

        if (fourth <= 0.37)
            genetics += "A";
        else if (fourth <= 0.58)
            genetics += "C";
        else if (fourth <= 0.63)
            genetics += "T";
        else
            genetics += "E";
        return genetics;

    }
//inherit genes from parents for new Persons
    public String inheritGenes(Person m, Person f) {
        String genetics = "";
        double first = rand.nextDouble();
        double second = rand.nextDouble();
        double third = rand.nextDouble();
        double fourth = rand.nextDouble();

        if (first < 0.5) {
            genetics += m.getGenes().charAt(0);
        } else
            genetics += f.getGenes().charAt(0);

        if (second < 0.5) {
            genetics += m.getGenes().charAt(1);
        } else
            genetics += f.getGenes().charAt(1);

        if (third < 0.5) {
            genetics += m.getGenes().charAt(2);
        } else
            genetics += f.getGenes().charAt(2);

        if (fourth < 0.5) {
            genetics += m.getGenes().charAt(3);
        } else
            genetics += f.getGenes().charAt(3);

        return genetics;
    }
}

List<Person> 中找到最常见基因的示例代码。我刚刚为 genes 添加了一个 getter String:

String getGenes() {
    return genes;
}

代码如下:

List<Person> people = new ArrayList<>();

for (int i = 0; i < 100; i++) {
    people.add(new Person()); // 100 random genes
}

String mostCommonGene = people.stream()
                .map(Person::getGenes)
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                .entrySet()
                .stream()
                .max(Comparator.comparingLong(Map.Entry::getValue))
                .get()
                .getKey();

    System.out.println("Most common gene: " + mostCommonGene);

我们使用 Java 8 个流:

  • 我们得到 people 列表的 stream()
  • 我们 map()(转换)每个 PersonString - 他们的 genes.
  • 我们 collect() 基因流 groupingBy()Function.identity()Collectors.counting() 喂养。此步骤生成一个 Map<String, Long>,它表示 map genes 及其频率。实际上,这计算了 people 列表中基因的出现次数。
  • 然后我们在该地图上调用 entrySet(),然后再次调用 stream() - 现在我们有一个 地图条目(您可以将它们视为成对 - 一个对象内的基因及其频率。方便)。
  • 我们调用 max() 来找到具有最高 value(解释为频率)的 entryComparator.comparingLong() 告诉 max() 算法我们如何比较这些对,但是 不是 longs - 这就是为什么我们必须告诉它如何将 entry 转换为 long - 我们得到 entryentry.
  • 然后我们调用 get(),因为 max() returns 和 Optional<T>。我们只想要 T条目)。
  • 最后,我们在 entry 上调用 getKey(),表示一对最频繁的基因及其频率。如前所述,键是基因,值是它的频率。

如果您不熟悉此答案中描述的大多数概念,我强烈建议您了解 Java 8 Streams。习惯了就停不下来了

根据使用场景,使用额外的 Map<String,Long> 作为每个基因的计数器(为每个新的 Person 基因增加计数器)可能会更有效。

这种方法会使用更多的内存(Map<String,Long> 最多有 256 个元素)并且新的 Person 构造会有点慢(地图的更新),但是在每个 tick 上获取最常见的基因如果有许多 Person 对象处于活动状态,可能会快得多,因为这些对象已经在 Map 中进行了分组和计数,因此最多只应比较 256 个条目。