diff --git a/algorithms/.gitignore b/algorithms/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/algorithms/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/algorithms/pom.xml b/algorithms/pom.xml index 529af19686..884c804d13 100644 --- a/algorithms/pom.xml +++ b/algorithms/pom.xml @@ -31,6 +31,11 @@ <version>${lombok.version}</version> <scope>provided</scope> </dependency> + <dependency> + <groupId>io.jenetics</groupId> + <artifactId>jenetics</artifactId> + <version>3.7.0</version> + </dependency> </dependencies> <build> diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/Knapsack.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/Knapsack.java new file mode 100644 index 0000000000..cc99ccf204 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/Knapsack.java @@ -0,0 +1,47 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static org.jenetics.engine.EvolutionResult.toBestPhenotype; +import static org.jenetics.engine.limit.bySteadyFitness; + +import java.util.stream.Stream; + +import org.jenetics.BitChromosome; +import org.jenetics.BitGene; +import org.jenetics.Mutator; +import org.jenetics.Phenotype; +import org.jenetics.RouletteWheelSelector; +import org.jenetics.SinglePointCrossover; +import org.jenetics.TournamentSelector; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionStatistics; + +//The main class. +public class Knapsack { + + public static void main(String[] args) { + int nItems = 15; + double ksSize = nItems * 100.0 / 3.0; + + KnapsackFF ff = new KnapsackFF(Stream.generate(KnapsackItem::random) + .limit(nItems) + .toArray(KnapsackItem[]::new), ksSize); + + Engine<BitGene, Double> engine = Engine.builder(ff, BitChromosome.of(nItems, 0.5)) + .populationSize(500) + .survivorsSelector(new TournamentSelector<>(5)) + .offspringSelector(new RouletteWheelSelector<>()) + .alterers(new Mutator<>(0.115), new SinglePointCrossover<>(0.16)) + .build(); + + EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber(); + + Phenotype<BitGene, Double> best = engine.stream() + .limit(bySteadyFitness(7)) + .limit(100) + .peek(statistics) + .collect(toBestPhenotype()); + + System.out.println(statistics); + System.out.println(best); + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackFF.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackFF.java new file mode 100644 index 0000000000..e3e06d301a --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackFF.java @@ -0,0 +1,25 @@ +package com.baeldung.algorithms.ga.jenetics; + +import java.util.function.Function; + +import org.jenetics.BitChromosome; +import org.jenetics.BitGene; +import org.jenetics.Genotype; + +public class KnapsackFF implements Function<Genotype<BitGene>, Double> { + private KnapsackItem[] items; + private double size; + + public KnapsackFF(KnapsackItem[] items, double size) { + this.items = items; + this.size = size; + } + + @Override + public Double apply(Genotype<BitGene> gt) { + KnapsackItem sum = ((BitChromosome) gt.getChromosome()).ones() + .mapToObj(i -> items[i]) + .collect(KnapsackItem.toSum()); + return sum.size <= this.size ? sum.value : 0; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackItem.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackItem.java new file mode 100644 index 0000000000..876df0ba25 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/KnapsackItem.java @@ -0,0 +1,34 @@ +package com.baeldung.algorithms.ga.jenetics; + +import java.util.Random; +import java.util.stream.Collector; + +import org.jenetics.util.RandomRegistry; + +public class KnapsackItem { + + public double size; + public double value; + + public KnapsackItem(double size, double value) { + this.size = size; + this.value = value; + } + + protected static KnapsackItem random() { + Random r = RandomRegistry.getRandom(); + return new KnapsackItem(r.nextDouble() * 100, r.nextDouble() * 100); + } + + protected static Collector<KnapsackItem, ?, KnapsackItem> toSum() { + return Collector.of(() -> new double[2], (a, b) -> { + a[0] += b.size; + a[1] += b.value; + } , (a, b) -> { + a[0] += b[0]; + a[1] += b[1]; + return a; + } , r -> new KnapsackItem(r[0], r[1])); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SimpleGeneticAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SimpleGeneticAlgorithm.java new file mode 100644 index 0000000000..845e11b349 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SimpleGeneticAlgorithm.java @@ -0,0 +1,33 @@ +package com.baeldung.algorithms.ga.jenetics; + +import org.jenetics.BitChromosome; +import org.jenetics.BitGene; +import org.jenetics.Genotype; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionResult; +import org.jenetics.util.Factory; + +public class SimpleGeneticAlgorithm { + + private static Integer eval(Genotype<BitGene> gt) { + return gt.getChromosome() + .as(BitChromosome.class) + .bitCount(); + } + + public static void main(String[] args) { + Factory<Genotype<BitGene>> gtf = Genotype.of(BitChromosome.of(10, 0.5)); + System.out.println("Before the evolution:\n" + gtf); + + Engine<BitGene, Integer> engine = Engine.builder(SimpleGeneticAlgorithm::eval, gtf) + .build(); + + Genotype<BitGene> result = engine.stream() + .limit(500) + .collect(EvolutionResult.toBestGenotype()); + + System.out.println("After the evolution:\n" + result); + + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenProblem.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenProblem.java new file mode 100644 index 0000000000..55f2f7af0a --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenProblem.java @@ -0,0 +1,86 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.util.Objects.requireNonNull; + +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jenetics.BitGene; +import org.jenetics.engine.Codec; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionResult; +import org.jenetics.engine.Problem; +import org.jenetics.engine.codecs; +import org.jenetics.util.ISeq; + +public class SpringsteenProblem implements Problem<ISeq<SpringsteenRecord>, BitGene, Double> { + + private ISeq<SpringsteenRecord> records; + private double maxPricePerUniqueSong; + + public SpringsteenProblem(ISeq<SpringsteenRecord> records, double maxPricePerUniqueSong) { + this.records = requireNonNull(records); + this.maxPricePerUniqueSong = maxPricePerUniqueSong; + } + + @Override + public Function<ISeq<SpringsteenRecord>, Double> fitness() { + return SpringsteenRecords -> { + double cost = SpringsteenRecords.stream() + .mapToDouble(r -> r.price) + .sum(); + + int uniqueSongCount = SpringsteenRecords.stream() + .flatMap(r -> r.songs.stream()) + .collect(Collectors.toSet()) + .size(); + + double pricePerUniqueSong = cost / uniqueSongCount; + + return pricePerUniqueSong <= maxPricePerUniqueSong ? uniqueSongCount : 0.0; + }; + } + + @Override + public Codec<ISeq<SpringsteenRecord>, BitGene> codec() { + return codecs.ofSubSet(records); + } + + public static void main(String[] args) { + double maxPricePerUniqueSong = 2.5; + + SpringsteenProblem springsteen = new SpringsteenProblem( + ISeq.of(new SpringsteenRecord("SpringsteenRecord1", 25, ISeq.of("Song1", "Song2", "Song3", "Song4", "Song5", "Song6")), new SpringsteenRecord("SpringsteenRecord2", 15, ISeq.of("Song2", "Song3", "Song4", "Song5", "Song6", "Song7")), + new SpringsteenRecord("SpringsteenRecord3", 35, ISeq.of("Song5", "Song6", "Song7", "Song8", "Song9", "Song10")), new SpringsteenRecord("SpringsteenRecord4", 17, ISeq.of("Song9", "Song10", "Song12", "Song4", "Song13", "Song14")), + new SpringsteenRecord("SpringsteenRecord5", 29, ISeq.of("Song1", "Song2", "Song13", "Song14", "Song15", "Song16")), new SpringsteenRecord("SpringsteenRecord6", 5, ISeq.of("Song18", "Song20", "Song30", "Song40"))), + maxPricePerUniqueSong); + + Engine<BitGene, Double> engine = Engine.builder(springsteen) + .build(); + + ISeq<SpringsteenRecord> result = springsteen.codec() + .decoder() + .apply(engine.stream() + .limit(10) + .collect(EvolutionResult.toBestGenotype())); + + double cost = result.stream() + .mapToDouble(r -> r.price) + .sum(); + + int uniqueSongCount = result.stream() + .flatMap(r -> r.songs.stream()) + .collect(Collectors.toSet()) + .size(); + + double pricePerUniqueSong = cost / uniqueSongCount; + + System.out.println("Overall cost: " + cost); + System.out.println("Unique songs: " + uniqueSongCount); + System.out.println("Cost per song: " + pricePerUniqueSong); + System.out.println("Records: " + result.map(r -> r.name) + .toString(", ")); + + } + +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenRecord.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenRecord.java new file mode 100644 index 0000000000..b49709e7f5 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SpringsteenRecord.java @@ -0,0 +1,24 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.util.Objects.requireNonNull; + +import org.jenetics.util.ISeq; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class SpringsteenRecord { + + String name; + double price; + ISeq<String> songs; + + public SpringsteenRecord(String name, double price, ISeq<String> songs) { + this.name = requireNonNull(name); + this.price = price; + this.songs = requireNonNull(songs); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SubsetSum.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SubsetSum.java new file mode 100644 index 0000000000..db1e11239f --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/SubsetSum.java @@ -0,0 +1,66 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.util.Objects.requireNonNull; + +import java.util.Random; +import java.util.function.Function; + +import org.jenetics.EnumGene; +import org.jenetics.Mutator; +import org.jenetics.PartiallyMatchedCrossover; +import org.jenetics.Phenotype; +import org.jenetics.engine.Codec; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionResult; +import org.jenetics.engine.Problem; +import org.jenetics.engine.codecs; +import org.jenetics.engine.limit; +import org.jenetics.util.ISeq; +import org.jenetics.util.LCG64ShiftRandom; + +public class SubsetSum implements Problem<ISeq<Integer>, EnumGene<Integer>, Integer> { + + private ISeq<Integer> basicSet; + private int size; + + public SubsetSum(ISeq<Integer> basicSet, int size) { + this.basicSet = requireNonNull(basicSet); + this.size = size; + } + + @Override + public Function<ISeq<Integer>, Integer> fitness() { + return subset -> Math.abs(subset.stream() + .mapToInt(Integer::intValue) + .sum()); + } + + @Override + public Codec<ISeq<Integer>, EnumGene<Integer>> codec() { + return codecs.ofSubSet(basicSet, size); + } + + public static SubsetSum of(int n, int k, Random random) { + return new SubsetSum(random.doubles() + .limit(n) + .mapToObj(d -> (int) ((d - 0.5) * n)) + .collect(ISeq.toISeq()), k); + } + + public static void main(String[] args) { + SubsetSum problem = of(500, 15, new LCG64ShiftRandom(101010)); + + Engine<EnumGene<Integer>, Integer> engine = Engine.builder(problem) + .minimizing() + .maximalPhenotypeAge(5) + .alterers(new PartiallyMatchedCrossover<>(0.4), new Mutator<>(0.3)) + .build(); + + Phenotype<EnumGene<Integer>, Integer> result = engine.stream() + .limit(limit.bySteadyFitness(55)) + .collect(EvolutionResult.toBestPhenotype()); + + System.out.print(result); + } + +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/TravelingSalesman.java b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/TravelingSalesman.java new file mode 100644 index 0000000000..80ede0f8c5 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/ga/jenetics/TravelingSalesman.java @@ -0,0 +1,67 @@ +package com.baeldung.algorithms.ga.jenetics; + +import static java.lang.Math.PI; +import static java.lang.Math.abs; +import static java.lang.Math.sin; +import static org.jenetics.engine.EvolutionResult.toBestPhenotype; +import static org.jenetics.engine.limit.bySteadyFitness; + +import java.util.stream.IntStream; + +import org.jenetics.EnumGene; +import org.jenetics.Optimize; +import org.jenetics.PartiallyMatchedCrossover; +import org.jenetics.Phenotype; +import org.jenetics.SwapMutator; +import org.jenetics.engine.Engine; +import org.jenetics.engine.EvolutionStatistics; +import org.jenetics.engine.codecs; + +public class TravelingSalesman { + + private static final int STOPS = 50; + private static final double[][] ADJACENCE = matrix(STOPS); + + private static double[][] matrix(int stops) { + final double radius = 100.0; + double[][] matrix = new double[stops][stops]; + + for (int i = 0; i < stops; ++i) { + for (int j = 0; j < stops; ++j) { + matrix[i][j] = chord(stops, abs(i - j), radius); + } + } + return matrix; + } + + private static double chord(int stops, int i, double r) { + return 2.0 * r * abs(sin(PI * i / stops)); + } + + private static double dist(final int[] path) { + return IntStream.range(0, STOPS) + .mapToDouble(i -> ADJACENCE[path[i]][path[(i + 1) % STOPS]]) + .sum(); + } + + public static void main(String[] args) { + final Engine<EnumGene<Integer>, Double> engine = Engine.builder(TravelingSalesman::dist, codecs.ofPermutation(STOPS)) + .optimize(Optimize.MINIMUM) + .maximalPhenotypeAge(11) + .populationSize(500) + .alterers(new SwapMutator<>(0.2), new PartiallyMatchedCrossover<>(0.35)) + .build(); + + final EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber(); + + final Phenotype<EnumGene<Integer>, Double> best = engine.stream() + .limit(bySteadyFitness(15)) + .limit(250) + .peek(statistics) + .collect(toBestPhenotype()); + + System.out.println(statistics); + System.out.println(best); + } + +}