/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ Franz Wilhelmstötter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) */ package org.jenetics; import static java.lang.Math.min; import static java.lang.String.format; import java.util.Random; import org.jenetics.internal.math.base; import org.jenetics.internal.util.Equality; import org.jenetics.internal.util.Hash; import org.jenetics.util.MSeq; import org.jenetics.util.RandomRegistry; /** * <p><strong>Multiple point crossover</strong></p> * * If the {@code MultiPointCrossover} is created with one crossover point, it * behaves exactly like the {@link SinglePointCrossover}. The following picture * shows how the {@code MultiPointCrossover} works with two crossover points, * defined at index 1 and 4. * <p> * <img src="doc-files/2PointCrossover.svg" width="400" alt="2-point crossover"> * </p> * * If the number of crossover points is odd, the crossover looks like in the * following figure. * * <p> * <img src="doc-files/3PointCrossover.svg" width="400" alt="3-point crossover"> * </p> * * @see SinglePointCrossover * * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> * @since 1.2 * @version 3.6 */ public class MultiPointCrossover< G extends Gene<?, G>, C extends Comparable<? super C> > extends Crossover<G, C> { private final int _n; /** * Create a new crossover instance. * * @param probability the recombination probability. * @param n the number of crossover points. * @throws IllegalArgumentException if the {@code probability} is not in the * valid range of {@code [0, 1]} or {@code n < 1}. */ public MultiPointCrossover(final double probability, final int n) { super(probability); if (n < 1) { throw new IllegalArgumentException(format( "n must be at least 1 but was %d.", n )); } _n = n; } /** * Create a new crossover instance with two crossover points. * * @param probability the recombination probability. * @throws IllegalArgumentException if the {@code probability} is not in the * valid range of {@code [0, 1]}. */ public MultiPointCrossover(final double probability) { this(probability, 2); } /** * Create a new crossover instance with default crossover probability of * 0.05. * * @param n the number of crossover points. * @throws IllegalArgumentException if {@code n < 1}. */ public MultiPointCrossover(final int n) { this(0.05, n); } /** * Create a new crossover instance with two crossover points and crossover * probability 0.05. */ public MultiPointCrossover() { this(0.05, 2); } /** * Return the number of crossover points. * * @return the number of crossover points. */ public int getN() { return _n; } @Override protected int crossover(final MSeq<G> that, final MSeq<G> other) { assert that.length() == other.length(); final int n = min(that.length(), other.length()); final int k = min(n, _n); final Random random = RandomRegistry.getRandom(); final int[] points = k > 0 ? base.subset(n, k, random) : new int[0]; crossover(that, other, points); return 2; } // Package private for testing purpose. static <T> void crossover( final MSeq<T> that, final MSeq<T> other, final int[] indexes ) { for (int i = 0; i < indexes.length - 1; i += 2) { final int start = indexes[i]; final int end = indexes[i + 1]; that.swap(start, end, other, start); } if (indexes.length%2 == 1) { final int index = indexes[indexes.length - 1]; that.swap(index, min(that.length(), other.length()), other, index); } } @Override public int hashCode() { return Hash.of(getClass()) .and(super.hashCode()) .and(_n).value(); } @Override public boolean equals(final Object obj) { return Equality.of(this, obj).test(mpc -> _n == mpc._n && super.equals(obj) ); } @Override public String toString() { return format( "%s[p=%f, n=%d]", getClass().getSimpleName(), _probability, _n ); } //public static <G extends Gene<?, G>> MultiPointCrossover<G> zip() { // return new MultiPointCrossover<>(Integer.MAX_VALUE); //} }