diff --git a/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java b/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java index 3f7c8bf4b3..22c5776293 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java +++ b/core-java/src/main/java/com/baeldung/algorithms/RunAlgorithm.java @@ -2,7 +2,8 @@ package com.baeldung.algorithms; import java.util.Scanner; -import com.baeldung.algorithms.annealing.SimulatedAnnealing; +import com.baeldung.algorithms.ga.annealing.SimulatedAnnealing; +import com.baeldung.algorithms.ga.ant_colony.AntColonyOptimization; import com.baeldung.algorithms.ga.binary.SimpleGeneticAlgorithm; import com.baeldung.algorithms.slope_one.SlopeOne; @@ -14,6 +15,7 @@ public class RunAlgorithm { System.out.println("1 - Simulated Annealing"); System.out.println("2 - Slope One"); System.out.println("3 - Simple Genetic Algorithm"); + System.out.println("4 - Ant Colony"); int decision = in.nextInt(); switch (decision) { case 1: @@ -27,6 +29,10 @@ public class RunAlgorithm { SimpleGeneticAlgorithm ga = new SimpleGeneticAlgorithm(); ga.runAlgorithm(50, "1011000100000100010000100000100111001000000100000100000000001111"); break; + case 4: + AntColonyOptimization antColony = new AntColonyOptimization(21); + antColony.startAntOptimization(); + break; default: System.out.println("Unknown option"); break; diff --git a/core-java/src/main/java/com/baeldung/algorithms/annealing/City.java b/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/City.java similarity index 90% rename from core-java/src/main/java/com/baeldung/algorithms/annealing/City.java rename to core-java/src/main/java/com/baeldung/algorithms/ga/annealing/City.java index 77e8652df0..cb5647f4d2 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/annealing/City.java +++ b/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/City.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms.annealing; +package com.baeldung.algorithms.ga.annealing; import lombok.Data; diff --git a/core-java/src/main/java/com/baeldung/algorithms/annealing/SimulatedAnnealing.java b/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/SimulatedAnnealing.java similarity index 96% rename from core-java/src/main/java/com/baeldung/algorithms/annealing/SimulatedAnnealing.java rename to core-java/src/main/java/com/baeldung/algorithms/ga/annealing/SimulatedAnnealing.java index a7dc974e97..bff64fc239 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/annealing/SimulatedAnnealing.java +++ b/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/SimulatedAnnealing.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms.annealing; +package com.baeldung.algorithms.ga.annealing; public class SimulatedAnnealing { diff --git a/core-java/src/main/java/com/baeldung/algorithms/annealing/Travel.java b/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/Travel.java similarity index 97% rename from core-java/src/main/java/com/baeldung/algorithms/annealing/Travel.java rename to core-java/src/main/java/com/baeldung/algorithms/ga/annealing/Travel.java index 9bf341fbbe..3139b49586 100644 --- a/core-java/src/main/java/com/baeldung/algorithms/annealing/Travel.java +++ b/core-java/src/main/java/com/baeldung/algorithms/ga/annealing/Travel.java @@ -1,4 +1,4 @@ -package com.baeldung.algorithms.annealing; +package com.baeldung.algorithms.ga.annealing; import java.util.ArrayList; import java.util.Collections; diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/Ant.java b/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/Ant.java new file mode 100644 index 0000000000..4ea23b799f --- /dev/null +++ b/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/Ant.java @@ -0,0 +1,37 @@ +package com.baeldung.algorithms.ga.ant_colony; + +public class Ant { + + protected int trailSize; + protected int trail[]; + protected boolean visited[]; + + public Ant(int tourSize) { + this.trailSize = tourSize; + this.trail = new int[tourSize]; + this.visited = new boolean[tourSize]; + } + + protected void visitCity(int currentIndex, int city) { + trail[currentIndex + 1] = city; + visited[city] = true; + } + + protected boolean visited(int i) { + return visited[i]; + } + + protected double trailLength(double graph[][]) { + double length = graph[trail[trailSize - 1]][trail[0]]; + for (int i = 0; i < trailSize - 1; i++) { + length += graph[trail[i]][trail[i + 1]]; + } + return length; + } + + protected void clear() { + for (int i = 0; i < trailSize; i++) + visited[i] = false; + } + +} diff --git a/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java b/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java new file mode 100644 index 0000000000..e46ac77e84 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/algorithms/ga/ant_colony/AntColonyOptimization.java @@ -0,0 +1,212 @@ +package com.baeldung.algorithms.ga.ant_colony; + +import java.util.Arrays; +import java.util.Random; + +public class AntColonyOptimization { + + private double c = 1.0; + private double alpha = 1; + private double beta = 5; + private double evaporation = 0.5; + private double Q = 500; + private double antFactor = 0.8; + private double randomFactor = 0.01; + + private int maxIterations = 1000; + + public int numberOfCities; + public int numberOfAnts; + private double graph[][]; + private double trails[][]; + private Ant ants[]; + private Random random = new Random(); + private double probabilities[]; + + private int currentIndex; + + public int[] bestTourOrder; + public double bestTourLength; + + public AntColonyOptimization(int noOfCities) { + graph = generateRandomMatrix(noOfCities); + numberOfCities = graph.length; + numberOfAnts = (int) (numberOfCities * antFactor); + + trails = new double[numberOfCities][numberOfCities]; + probabilities = new double[numberOfCities]; + ants = new Ant[numberOfAnts]; + for (int j = 0; j < numberOfAnts; j++) { + ants[j] = new Ant(numberOfCities); + } + } + + /** + * Generate initial solution + * @param n + * @return + */ + public double[][] generateRandomMatrix(int n) { + double[][] randomMatrix = new double[n][n]; + random.setSeed(System.currentTimeMillis()); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + Integer r = random.nextInt(100) + 1; + randomMatrix[i][j] = Math.abs(r); + } + } + return randomMatrix; + } + + /** + * Perform ant optimization + * @return + */ + public int[] startAntOptimization() { + int[] finalResult = null; + for (int i = 1; i <= 3; i++) { + System.out.println("Attempt #" + i); + finalResult = solve(); + } + return finalResult; + } + + /** + * Use this method to run the main logic + * @return + */ + private int[] solve() { + setupAnts(); + clearTrails(); + int iteration = 0; + while (iteration < maxIterations) { + moveAnts(); + updateTrails(); + updateBest(); + iteration++; + } + System.out.println("Best tour length: " + (bestTourLength - numberOfCities)); + System.out.println("Best tour order: " + Arrays.toString(bestTourOrder)); + return bestTourOrder.clone(); + } + + /** + * Prepare ants for the simulation + */ + private void setupAnts() { + currentIndex = -1; + for (int i = 0; i < numberOfAnts; i++) { + ants[i].clear(); + ants[i].visitCity(currentIndex, random.nextInt(numberOfCities)); + } + currentIndex++; + } + + /** + * At each iteration, move ants + */ + private void moveAnts() { + while (currentIndex < numberOfCities - 1) { + for (Ant a : ants) + a.visitCity(currentIndex, selectNextCity(a)); + currentIndex++; + } + } + + /** + * Select next city for each ant + * @param ant + * @return + */ + private int selectNextCity(Ant ant) { + if (random.nextDouble() < randomFactor) { + int t = random.nextInt(numberOfCities - currentIndex); + int j = -1; + for (int i = 0; i < numberOfCities; i++) { + if (!ant.visited(i)) { + j++; + } + if (j == t) { + return i; + } + } + } + calculateProbabilities(ant); + double r = random.nextDouble(); + double total = 0; + for (int i = 0; i < numberOfCities; i++) { + total += probabilities[i]; + if (total >= r) { + return i; + } + } + + throw new RuntimeException("There are no other cities"); + } + + /** + * Calculate the next city picks probabilites + * @param ant + */ + private void calculateProbabilities(Ant ant) { + int i = ant.trail[currentIndex]; + double pheromone = 0.0; + for (int l = 0; l < numberOfCities; l++) { + if (!ant.visited(l)) { + pheromone += Math.pow(trails[i][l], alpha) * Math.pow(1.0 / graph[i][l], beta); + } + } + for (int j = 0; j < numberOfCities; j++) { + if (ant.visited(j)) { + probabilities[j] = 0.0; + } else { + double numerator = Math.pow(trails[i][j], alpha) * Math.pow(1.0 / graph[i][j], beta); + probabilities[j] = numerator / pheromone; + } + } + } + + /** + * Update trails that ants used + */ + private void updateTrails() { + for (int i = 0; i < numberOfCities; i++) { + for (int j = 0; j < numberOfCities; j++) { + trails[i][j] *= evaporation; + } + } + for (Ant a : ants) { + double contribution = Q / a.trailLength(graph); + for (int i = 0; i < numberOfCities - 1; i++) { + trails[a.trail[i]][a.trail[i + 1]] += contribution; + } + trails[a.trail[numberOfCities - 1]][a.trail[0]] += contribution; + } + } + + /** + * Update the best solution + */ + private void updateBest() { + if (bestTourOrder == null) { + bestTourOrder = ants[0].trail; + bestTourLength = ants[0].trailLength(graph); + } + for (Ant a : ants) { + if (a.trailLength(graph) < bestTourLength) { + bestTourLength = a.trailLength(graph); + bestTourOrder = a.trail.clone(); + } + } + } + + /** + * Clear trails after simulation + */ + private void clearTrails() { + for (int i = 0; i < numberOfCities; i++) + for (int j = 0; j < numberOfCities; j++) + trails[i][j] = c; + } + +} diff --git a/core-java/src/test/java/com/baeldung/algorithms/AntColonyOptimizationTest.java b/core-java/src/test/java/com/baeldung/algorithms/AntColonyOptimizationTest.java new file mode 100644 index 0000000000..cd8efaa106 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/algorithms/AntColonyOptimizationTest.java @@ -0,0 +1,22 @@ +package com.baeldung.algorithms; + +import org.junit.Assert; +import org.junit.Test; + +import com.baeldung.algorithms.ga.ant_colony.AntColonyOptimization; + +public class AntColonyOptimizationTest { + + @Test + public void testGenerateRandomMatrix() { + AntColonyOptimization antTSP = new AntColonyOptimization(5); + Assert.assertNotNull(antTSP.generateRandomMatrix(5)); + } + + @Test + public void testStartAntOptimization() { + AntColonyOptimization antTSP = new AntColonyOptimization(5); + Assert.assertNotNull(antTSP.startAntOptimization()); + } + +} diff --git a/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java b/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java index 2fc7ea9b92..6822bae990 100644 --- a/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java +++ b/core-java/src/test/java/com/baeldung/algorithms/SimulatedAnnealingTest.java @@ -3,7 +3,7 @@ package com.baeldung.algorithms; import org.junit.Assert; import org.junit.Test; -import com.baeldung.algorithms.annealing.SimulatedAnnealing; +import com.baeldung.algorithms.ga.annealing.SimulatedAnnealing; public class SimulatedAnnealingTest {