【问题标题】:Genetic Algorithm Fitness and Cross Over selection遗传算法适应度和交叉选择
【发布时间】:2020-10-14 06:10:27
【问题描述】:

我正在尝试使用遗传算法为教室中的给定部分安排随机座位列表,以便该教室中的所有小组坐在一起。

这是我目前的尝试,我不清楚改进此算法的最佳方法,因为这是我学习 GA 的早期阶段。

我正在使用 Java 库 Jenetics,它非常适合我入门,这里是引擎。

        final Engine<EnumGene<Seat>, Double> ENGINE = Engine
                .builder(SeatingFitness::fitness, encoding)
                .populationSize(200)
                .selector(new RouletteWheelSelector<>())
                .alterers(
                        new PartiallyMatchedCrossover<>(0.2),
                        new Mutator<>(0.01)
                )
                .optimize(Optimize.MINIMUM)
                .build();

编码是这样的

    static final ISeq<Seat> seats = ISeq.of(createSeats());
    static final Codec<ISeq<Seat>, EnumGene<Seat>> encoding = Codecs.ofPermutation(seats);

    public static List<Seat> createSeats() {
        return new ArrayList<>(Arrays.asList(
                new Seat("Group C", 1),
                new Seat("Group A", 2),
                new Seat("Group B", 3)
                .....more seats here....)
    }

我的健身功能肯定可以提高,我没有使用任何库,所以这里的任何建议都会很棒,但看起来像这样。

基本上我所做的只是找到组中每个座位的 x 和 y 坐标,并计算每个座位与组中其他座位的距离,并将这些值相加。值越低越好,因此引擎中的Optimize.MINIMUM

private static int numberOfSeatsInRow = 8;

    public static double fitness(final ISeq<Seat> seats) {

        AtomicDouble score = new AtomicDouble();
        // Group together all the seats that belong to a particular group.
        Map<String, List<Seat>> grouping = seats.stream().collect(groupingBy(Seat::getGroup));

        grouping.forEach((group, groupsSeats) -> {
            // Find the location in the overall list of the seats for the group.
            if (!group.equals(Seat.EMPTY_SEAT)) {
                List<Integer> indexOfSeatInOverallList = groupsSeats.stream().map(seats::indexOf).collect(Collectors.toList());
                if (indexOfSeatInOverallList.size() > 2) {
                    // Get the first element positioned correctly on the x and y axis

                    double totalCalculated = indexOfSeatInOverallList.stream().reduce(0, (subTotal, currentElement) -> {

                        int xReferenceCoordinate = calculateXCoordinate(currentElement);
                        int yReferenceCoordinate = calculateYCoordinate(currentElement);

                        double totalDistance = 0;
                        int multiplier = groupsSeats.size() <= numberOfSeatsInRow ? 10 : 500;
                        for (Integer integer : indexOfSeatInOverallList) {
                            int xSecondary = calculateXCoordinate(integer);
                            int ySecondary = calculateYCoordinate(integer);
                            if (ySecondary != yReferenceCoordinate) {
                                totalDistance += multiplier * Math.abs(yReferenceCoordinate - ySecondary);
                            }
                            totalDistance += calculateDistanceBetweenTwoPoints(xReferenceCoordinate, yReferenceCoordinate, xSecondary, ySecondary);
                        }

                        return (int) totalDistance;
                    });

                    score.getAndAdd(totalCalculated);
                }
            }

        });
        return score.get();
    }

    private static int calculateXCoordinate(int positionInList) {
        int xPosition = positionInList % numberOfSeatsInRow;
        if (xPosition == 0) {
            xPosition = numberOfSeatsInRow;
        }
        return xPosition;
    }

    private static int calculateYCoordinate(int positionInList) {
        int xPosition = positionInList % numberOfSeatsInRow;
        int yPosition = positionInList / numberOfSeatsInRow;
        if (xPosition == 0) {
            yPosition = yPosition - 1;
        }

        return yPosition + 1;
    }

    private static double calculateDistanceBetweenTwoPoints(int x1, int y1, int x2, int y2) {
       // https://dqydj.com/2d-distance-calculator/
        double xValue = Math.pow((x2 - x1), 2);
        double yValue = Math.pow((y2 - y1), 2);
        return Math.sqrt(xValue + yValue);
    }

请看下面的结果图片,你可以看到它非常好(尽管运行大约需要 3 分钟才能产生正确的结果)。

【问题讨论】:

    标签: genetic-algorithm jenetics


    【解决方案1】:

    我查看了适应度函数。您为每个适应度函数调用计算的某些内容可以计算一次。

    private static final ISeq<Seat> SEATS = ISeq.of(
        new Seat("Group C", 1),
        new Seat("Group A", 2),
        new Seat("Group B", 3)
    );
    
    private static final Map<String, List<Seat>> SEAT_GROUPS = SEATS.stream()
        .collect(groupingBy(Seat::getGroup));
    

    SEAT_GROUPS 映射由席位列表定义,不会更改。如果我是对的,您的适应度函数中的 reduce 函数会忽略之前计算的距离。

    double totalCalculated = indexOfSeatInOverallList.stream()
        .reduce(0, (subTotal, currentElement) -> {
                // subTotal is ignored in your code, but should be added to the result.
                return (int) totalDistance + subTotal;
            })
    

    您的calculateDistanceBetweenTwoPoints 可以实现为

    double distance(final int x1, final int y1, final int x2, final int y2) {
        // sqrt(x^2 + y^2)
        return Math.hypot(x2 - x1, y2 - y1);
    }
    

    我的“清理”版本将如下所示。

    private static final int SEATS_PER_ROW = 8;
    
    private static final ISeq<Seat> SEATS = ISeq.of(
        new Seat("Group C", 1),
        new Seat("Group A", 2),
        new Seat("Group B", 3)
    );
    
    private static final Map<String, List<Seat>> SEAT_GROUPS = SEATS.stream()
        .collect(groupingBy(Seat::getGroup));
    
    public static double fitness(final ISeq<Seat> seats) {
        double score = 0;
    
        for (var entry : SEAT_GROUPS.entrySet()) {
            final var group = entry.getKey();
            final var groupsSeats = entry.getValue();
            final int multiplier = groupsSeats.size() <= SEATS_PER_ROW ? 10 : 500;
    
            if (!group.equals(Seat.EMPTY_SEAT)) {
                final int[] indexes = groupsSeats.stream()
                    .mapToInt(seats::indexOf)
                    .toArray();
    
                if (indexes.length > 2) {
                    final double dist = IntStream.of(indexes)
                        .reduce(0, (a, b) -> toDistance(multiplier, indexes, a, b));
    
                    score += dist;
                }
            }
        }
    
        return score;
    }
    
    private static int toDistance(
        final int multiplier,
        final int[] indexes,
        final int sum,
        final int index
    ) {
        final int x1 = toX(index);
        final int y = toY(index);
    
        int total = 0;
        for (int i : indexes) {
            final int x2 = toX(i);
            final int y2 = toY(i);
            if (y2 != y) {
                total += multiplier*Math.abs(y - y2);
            }
            total += distance(x1, y, x2, y2);
        }
    
        return sum + total;
    }
    
    private static double distance(final int x1, final int y1, final int x2, final int y2) {
        // sqrt(x^2 + y^2)
        return Math.hypot(x2 - x1, y2 - y1);
    }
    
    private static int toX(final int index) {
        int x = index%SEATS_PER_ROW;
        if (x == 0) {
            x = SEATS_PER_ROW;
        }
        return x;
    }
    
    private static int toY(final int index) {
        final int x = index%SEATS_PER_ROW;
        int y = index/SEATS_PER_ROW;
        if (x == 0) {
            y = y - 1;
        }
        return y + 1;
    }
    

    【讨论】:

      【解决方案2】:

      我可以看到关于您的Engine 定义的一点是您正在使用Mutator 更改器来解决组合问题。这会导致您的表现不佳(很可能)。 Mutator 不遵守您染色体中的 unique element 属性。突变染色体意味着使其无效,这会导致新染色体的不断重建。对于组合问题,只有SwapMutatorPartiallyMatchedCrossover有效的 修改器。

      final Engine<EnumGene<Seat>, Double> ENGINE = Engine
          .builder(SeatingFitness::fitness, encoding)
          .populationSize(200)
          .selector(new RouletteWheelSelector<>())
          .alterers(
              new PartiallyMatchedCrossover<>(0.2),
              new SwapMutator<>(0.01))
          .optimize(Optimize.MINIMUM)
          .build();
      

      您的适应度函数也可能会有所改进,但要对此发表评论,整个适应度函数代码会很好。

      【讨论】:

      • 感谢您的回复,这似乎对性能有所帮助。我正在编辑帖子以显示整个健身功能。
      猜你喜欢
      • 2011-10-07
      • 2016-09-16
      • 2014-08-12
      • 2014-12-21
      • 2019-05-26
      • 2011-12-21
      • 1970-01-01
      • 2012-12-10
      相关资源
      最近更新 更多