001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.0). 003 * Copyright (c) 2007-2024 Franz Wilhelmstötter 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 * Author: 018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com) 019 */ 020package io.jenetics; 021 022import static java.lang.String.format; 023import static io.jenetics.internal.math.Randoms.indexes; 024 025import java.util.random.RandomGenerator; 026 027import io.jenetics.internal.math.Subsets; 028import io.jenetics.util.MSeq; 029import io.jenetics.util.RandomRegistry; 030import io.jenetics.util.Seq; 031 032/** 033 * <p> 034 * An enhanced genetic algorithm (EGA) combine elements of existing solutions in 035 * order to create a new solution, with some of the properties of each parent. 036 * Recombination creates a new chromosome by combining parts of two (or more) 037 * parent chromosomes. This combination of chromosomes can be made by selecting 038 * one or more crossover points, splitting these chromosomes on the selected 039 * points, and merge those portions of different chromosomes to form new ones. 040 * </p> 041 * <p> 042 * The recombination probability <i>P(r)</i> determines the probability that a 043 * given individual (genotype, not gene) of a population is selected for 044 * recombination. The (<i>mean</i>) number of changed individuals depend on the 045 * concrete implementation and can be vary from 046 * <i>P(r)</i>·<i>N<sub>G</sub></i> to 047 * <i>P(r)</i>·<i>N<sub>G</sub></i>·<i>O<sub>R</sub></i>, where 048 * <i>O<sub>R</sub></i> is the order of the recombination, which is the number 049 * of individuals involved int the {@link #recombine} method. 050 * </p> 051 * 052 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 053 * @since 1.0 054 * @version 6.0 055 */ 056public abstract class Recombinator< 057 G extends Gene<?, G>, 058 C extends Comparable<? super C> 059> 060 extends AbstractAlterer<G, C> 061{ 062 063 private final int _order; 064 065 /** 066 * Constructs an alterer with a given recombination probability. 067 * 068 * @param probability The recombination probability. 069 * @param order the number of individuals involved in the 070 * {@link #recombine(MSeq, int[], long)} step 071 * @throws IllegalArgumentException if the {@code probability} is not in the 072 * valid range of {@code [0, 1]} or the given {@code order} is 073 * smaller than two. 074 */ 075 protected Recombinator(final double probability, final int order) { 076 super(probability); 077 if (order < 2) { 078 throw new IllegalArgumentException(format( 079 "Order must be greater than one, but was %d.", order 080 )); 081 } 082 _order = order; 083 } 084 085 /** 086 * Return the number of individuals involved in the 087 * {@link #recombine(MSeq, int[], long)} step. 088 * 089 * @return the number of individuals involved in the recombination step. 090 */ 091 public int order() { 092 return _order; 093 } 094 095 @Override 096 public final AltererResult<G, C> alter( 097 final Seq<Phenotype<G, C>> population, 098 final long generation 099 ) { 100 final AltererResult<G, C> result; 101 if (population.size() >= 2) { 102 final var random = RandomRegistry.random(); 103 final int order = Math.min(_order, population.size()); 104 105 final MSeq<Phenotype<G, C>> pop = MSeq.of(population); 106 final int count = indexes(random, population.size(), _probability) 107 .mapToObj(i -> individuals(i, population.size(), order, random)) 108 .mapToInt(ind -> recombine(pop, ind, generation)) 109 .sum(); 110 111 result = new AltererResult<>(pop.toISeq(), count); 112 } else { 113 result = new AltererResult<>(population.asISeq()); 114 } 115 116 return result; 117 } 118 119 static int[] individuals( 120 final int index, 121 final int size, 122 final int order, 123 final RandomGenerator random 124 ) { 125 final int[] ind = Subsets.next(random, size, order); 126 127 // Find the correct slot for the "master" individual. 128 // This prevents duplicate index entries. 129 int i = 0; 130 while (ind[i] < index && i < ind.length - 1) { 131 ++i; 132 } 133 ind[i] = index; 134 135 return ind; 136 } 137 138 /** 139 * Recombination template method. This method is called 0 to n times. It is 140 * guaranteed that this method is only called by one thread. 141 * 142 * @param population the population to recombine 143 * @param individuals the array with the indexes of the individuals which 144 * are involved in the <i>recombination</i> step. The length of the 145 * array is {@link #order()}. The first individual is the 146 * <i>primary</i> individual. 147 * @param generation the current generation. 148 * @return the number of genes that has been altered. 149 */ 150 protected abstract int recombine( 151 final MSeq<Phenotype<G, C>> population, 152 final int[] individuals, 153 final long generation 154 ); 155 156}