1 package net.bmahe.genetics4j.core.selection;
2
3 import java.util.Comparator;
4 import java.util.List;
5 import java.util.Objects;
6 import java.util.random.RandomGenerator;
7
8 import org.apache.commons.lang3.Validate;
9
10 import net.bmahe.genetics4j.core.Genotype;
11 import net.bmahe.genetics4j.core.Population;
12 import net.bmahe.genetics4j.core.spec.AbstractEAConfiguration;
13 import net.bmahe.genetics4j.core.spec.AbstractEAExecutionContext;
14 import net.bmahe.genetics4j.core.spec.selection.RouletteWheel;
15 import net.bmahe.genetics4j.core.spec.selection.SelectionPolicy;
16
17 public class RouletteWheelSelectionPolicyHandler<T extends Number & Comparable<T>>
18 implements SelectionPolicyHandler<T>
19 {
20
21 private final RandomGenerator randomGenerator;
22
23 public RouletteWheelSelectionPolicyHandler(final RandomGenerator _randomGenerator) {
24 Objects.requireNonNull(_randomGenerator);
25
26 this.randomGenerator = _randomGenerator;
27 }
28
29 @Override
30 public boolean canHandle(final SelectionPolicy selectionPolicy) {
31 Objects.requireNonNull(selectionPolicy);
32 return selectionPolicy instanceof RouletteWheel;
33 }
34
35 @Override
36 public Selector<T> resolve(AbstractEAExecutionContext<T> eaExecutionContext,
37 AbstractEAConfiguration<T> eaConfiguration, SelectionPolicyHandlerResolver<T> selectionPolicyHandlerResolver,
38 SelectionPolicy selectionPolicy) {
39 Objects.requireNonNull(selectionPolicy);
40 Validate.isInstanceOf(RouletteWheel.class, selectionPolicy);
41
42 return new Selector<>() {
43
44 @Override
45 public Population<T> select(final AbstractEAConfiguration<T> eaConfiguration, final long generation,
46 final int numIndividuals, final List<Genotype> population, final List<T> fitnessScore) {
47 Objects.requireNonNull(eaConfiguration);
48 Objects.requireNonNull(population);
49 Objects.requireNonNull(fitnessScore);
50 Validate.isTrue(numIndividuals > 0);
51 Validate.isTrue(population.size() == fitnessScore.size());
52
53 final Population<T> selectedIndividuals = new Population<>();
54
55 final double minFitness = fitnessScore.stream()
56 .map(Number::doubleValue)
57 .min(Comparator.naturalOrder())
58 .orElseThrow();
59 final double maxFitness = fitnessScore.stream()
60 .map(Number::doubleValue)
61 .max(Comparator.naturalOrder())
62 .orElseThrow();
63 final double reversedBase = minFitness + maxFitness;
64
65 double sumFitness = 0.0;
66 final double[] probabilities = new double[population.size()];
67
68 for (int i = 0; i < population.size(); i++) {
69 final double score = switch (eaConfiguration.optimization()) {
70 case MAXIMIZE -> fitnessScore.get(i).doubleValue();
71 case MINIMIZE -> reversedBase - fitnessScore.get(i).doubleValue();
72 };
73
74 sumFitness += score;
75 probabilities[i] = sumFitness;
76 }
77
78 for (int i = 0; i < numIndividuals; i++) {
79 final double targetScore = randomGenerator.nextDouble() * sumFitness;
80
81 int index = 0;
82 while (probabilities[index] < targetScore) {
83 index++;
84 }
85
86 selectedIndividuals.add(population.get(index), fitnessScore.get(index));
87 }
88
89 return selectedIndividuals;
90 }
91 };
92 }
93 }