001/* 002 * Java Genetic Algorithm Library (jenetics-8.3.0). 003 * Copyright (c) 2007-2025 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.Math.max; 023import static java.lang.Math.min; 024import static java.lang.String.format; 025import static java.util.Objects.requireNonNull; 026 027import io.jenetics.internal.util.Requires; 028import io.jenetics.util.ISeq; 029import io.jenetics.util.Seq; 030 031/** 032 * The {@code EliteSelector} copies a small proportion of the fittest candidates, 033 * without changes, into the next generation. This may have a dramatic impact on 034 * performance by ensuring that the GA doesn't waste time re-discovering 035 * previously refused partial solutions. Individuals that are preserved through 036 * elitism remain eligible for selection as parents of the next generation. 037 * Elitism is also related to memory: remember the best solution found so far. 038 * A problem with elitism is that it may cause the GA to converge to a local 039 * optimum, so pure elitism is a race to the nearest local optimum. 040 * {@snippet lang="java": 041 * final Selector<DoubleGene, Double> selector = new EliteSelector<>( 042 * // Number of the best individuals preserved for the next generation: elites 043 * 3, 044 * // Selector used for selecting rest of population. 045 * new RouletteWheelSelector<>() 046 * ); 047 * } 048 * 049 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 050 * @version 5.0 051 * @since 4.0 052 */ 053public class EliteSelector< 054 G extends Gene<?, G>, 055 C extends Comparable<? super C> 056> 057 implements Selector<G, C> 058{ 059 private final TruncationSelector<G, C> 060 ELITE_SELECTOR = new TruncationSelector<>(); 061 062 private final Selector<G, C> _nonEliteSelector; 063 private final int _eliteCount; 064 065 /** 066 * Create a new elite selector with the desired number of elites to be 067 * selected, and the selector used for selecting the rest of the population. 068 * 069 * @param eliteCount the desired number of elite individuals to be selected 070 * @param nonEliteSelector the selector used for selecting the rest of the 071 * population 072 * @throws IllegalArgumentException if {@code eliteCount < 1} 073 * @throws NullPointerException if the {@code nonEliteSelector} is 074 * {@code null} 075 */ 076 public EliteSelector( 077 final int eliteCount, 078 final Selector<G, C> nonEliteSelector 079 ) { 080 _eliteCount = Requires.positive(eliteCount); 081 _nonEliteSelector = requireNonNull(nonEliteSelector); 082 } 083 084 /** 085 * Create a new elite selector with the desired number of elites to be 086 * selected. The selector for selecting the rest of the population is 087 * initialized with {@code TournamentSelector<>(3)}. 088 * 089 * @see TournamentSelector 090 * 091 * @param eliteCount the desired number of elite individuals to be selected 092 * @throws IllegalArgumentException if {@code eliteCount < 1} 093 */ 094 public EliteSelector(final int eliteCount) { 095 this(eliteCount, new TournamentSelector<>(3)); 096 } 097 098 /** 099 * Create a new elite selector with selector used for selecting the rest of 100 * the population. The elite count is set to 1. 101 * 102 * @see TournamentSelector 103 * 104 * @param nonEliteSelector the selector used for selecting the rest of the 105 * population 106 * @throws NullPointerException if the {@code nonEliteSelector} is 107 * {@code null} 108 */ 109 public EliteSelector(final Selector<G, C> nonEliteSelector) { 110 this(1, nonEliteSelector); 111 } 112 113 /** 114 * Create a new elite selector with elite count 1, and the selector for 115 * selecting the rest of the population is initialized with 116 * {@code TournamentSelector<>(3)} 117 */ 118 public EliteSelector() { 119 this(1, new TournamentSelector<>(3)); 120 } 121 122 @Override 123 public ISeq<Phenotype<G, C>> select( 124 final Seq<Phenotype<G, C>> population, 125 final int count, 126 final Optimize opt 127 ) { 128 if (count < 0) { 129 throw new IllegalArgumentException(format( 130 "Selection count must be greater or equal then zero, but was %s.", 131 count 132 )); 133 } 134 135 ISeq<Phenotype<G, C>> result; 136 if (population.isEmpty() || count == 0) { 137 result = ISeq.empty(); 138 } else { 139 final int ec = min(count, _eliteCount); 140 result = ELITE_SELECTOR.select(population, ec, opt); 141 result = result.append( 142 _nonEliteSelector.select(population, max(0, count - ec), opt) 143 ); 144 } 145 146 return result; 147 } 148 149 @Override 150 public String toString() { 151 return format("EliteSelector[%d, %s]", _eliteCount, _nonEliteSelector); 152 } 153 154}