Final fixes on ACO (#1339)
* Ant Colony Optimization * Updated code for Ant Colony
This commit is contained in:
parent
18710230ab
commit
3abb98e9e8
|
@ -9,6 +9,7 @@
|
|||
<junit.version>4.12</junit.version>
|
||||
<maven-compiler-plugin.version>3.6.0</maven-compiler-plugin.version>
|
||||
<exec-maven-plugin.version>1.5.0</exec-maven-plugin.version>
|
||||
<lombok.version>1.16.12</lombok.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -18,6 +19,12 @@
|
|||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -9,13 +9,14 @@ import com.baeldung.algorithms.slope_one.SlopeOne;
|
|||
|
||||
public class RunAlgorithm {
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
|
||||
Scanner in = new Scanner(System.in);
|
||||
System.out.println("Run algorithm:");
|
||||
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");
|
||||
System.out.println("5 - Dijkstra");
|
||||
int decision = in.nextInt();
|
||||
switch (decision) {
|
||||
case 1:
|
||||
|
@ -33,6 +34,9 @@ public class RunAlgorithm {
|
|||
AntColonyOptimization antColony = new AntColonyOptimization(21);
|
||||
antColony.startAntOptimization();
|
||||
break;
|
||||
case 5:
|
||||
System.out.println("Please run the DijkstraAlgorithmTest.");
|
||||
break;
|
||||
default:
|
||||
System.out.println("Unknown option");
|
||||
break;
|
|
@ -0,0 +1,203 @@
|
|||
package com.baeldung.algorithms.ga.ant_colony;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
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;
|
||||
|
||||
private int numberOfCities;
|
||||
private int numberOfAnts;
|
||||
private double graph[][];
|
||||
private double trails[][];
|
||||
private List<Ant> ants = new ArrayList<>();
|
||||
private Random random = new Random();
|
||||
private double probabilities[];
|
||||
|
||||
private int currentIndex;
|
||||
|
||||
private int[] bestTourOrder;
|
||||
private 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];
|
||||
IntStream.range(0, numberOfAnts)
|
||||
.forEach(i -> ants.add(new Ant(numberOfCities)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate initial solution
|
||||
*/
|
||||
public double[][] generateRandomMatrix(int n) {
|
||||
double[][] randomMatrix = new double[n][n];
|
||||
IntStream.range(0, n)
|
||||
.forEach(i -> IntStream.range(0, n)
|
||||
.forEach(j -> randomMatrix[i][j] = Math.abs(random.nextInt(100) + 1)));
|
||||
return randomMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform ant optimization
|
||||
*/
|
||||
public void startAntOptimization() {
|
||||
IntStream.rangeClosed(1, 3)
|
||||
.forEach(i -> {
|
||||
System.out.println("Attempt #" + i);
|
||||
solve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to run the main logic
|
||||
*/
|
||||
public int[] solve() {
|
||||
setupAnts();
|
||||
clearTrails();
|
||||
IntStream.range(0, maxIterations)
|
||||
.forEach(i -> {
|
||||
moveAnts();
|
||||
updateTrails();
|
||||
updateBest();
|
||||
});
|
||||
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() {
|
||||
IntStream.range(0, numberOfAnts)
|
||||
.forEach(i -> {
|
||||
ants.forEach(ant -> {
|
||||
ant.clear();
|
||||
ant.visitCity(-1, random.nextInt(numberOfCities));
|
||||
});
|
||||
});
|
||||
currentIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* At each iteration, move ants
|
||||
*/
|
||||
private void moveAnts() {
|
||||
IntStream.range(currentIndex, numberOfCities - 1)
|
||||
.forEach(i -> {
|
||||
ants.forEach(ant -> ant.visitCity(currentIndex, selectNextCity(ant)));
|
||||
currentIndex++;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Select next city for each ant
|
||||
*/
|
||||
private int selectNextCity(Ant ant) {
|
||||
int t = random.nextInt(numberOfCities - currentIndex);
|
||||
if (random.nextDouble() < randomFactor) {
|
||||
OptionalInt cityIndex = IntStream.range(0, numberOfCities)
|
||||
.filter(i -> i == t && !ant.visited(i))
|
||||
.findFirst();
|
||||
if (cityIndex.isPresent()) {
|
||||
return cityIndex.getAsInt();
|
||||
}
|
||||
}
|
||||
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
|
||||
*/
|
||||
public 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.get(0).trail;
|
||||
bestTourLength = ants.get(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() {
|
||||
IntStream.range(0, numberOfCities)
|
||||
.forEach(i -> {
|
||||
IntStream.range(0, numberOfCities)
|
||||
.forEach(j -> trails[i][j] = c);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.algorithms.dijkstra;
|
||||
package com.baeldung.algorithms.ga.dijkstra;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.algorithms.dijkstra;
|
||||
package com.baeldung.algorithms.ga.dijkstra;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.algorithms.dijkstra;
|
||||
package com.baeldung.algorithms.ga.dijkstra;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.algorithms;
|
||||
package algorithms;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.algorithms;
|
||||
package algorithms;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
|
@ -1,10 +1,11 @@
|
|||
package algorithms;
|
||||
|
||||
import com.baeldung.algorithms.dijkstra.Dijkstra;
|
||||
import com.baeldung.algorithms.dijkstra.Graph;
|
||||
import com.baeldung.algorithms.dijkstra.Node;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.baeldung.algorithms.ga.dijkstra.Dijkstra;
|
||||
import com.baeldung.algorithms.ga.dijkstra.Graph;
|
||||
import com.baeldung.algorithms.ga.dijkstra.Node;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.algorithms;
|
||||
package algorithms;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
|
@ -1,189 +0,0 @@
|
|||
package com.baeldung.algorithms.ga.ant_colony;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
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;
|
||||
|
||||
private int numberOfCities;
|
||||
private int numberOfAnts;
|
||||
private double graph[][];
|
||||
private double trails[][];
|
||||
private List<Ant> ants = new ArrayList<>();
|
||||
private Random random = new Random();
|
||||
private double probabilities[];
|
||||
|
||||
private int currentIndex;
|
||||
|
||||
private int[] bestTourOrder;
|
||||
private 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];
|
||||
IntStream.range(0, numberOfAnts).forEach(i -> ants.add(new Ant(numberOfCities)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate initial solution
|
||||
*/
|
||||
public double[][] generateRandomMatrix(int n) {
|
||||
double[][] randomMatrix = new double[n][n];
|
||||
IntStream.range(0, n)
|
||||
.forEach(i -> IntStream.range(0, n)
|
||||
.forEach(j -> randomMatrix[i][j] = Math.abs(random.nextInt(100) + 1)));
|
||||
return randomMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform ant optimization
|
||||
*/
|
||||
public void startAntOptimization() {
|
||||
IntStream.rangeClosed(1, 3).forEach(i -> {
|
||||
System.out.println("Attempt #" + i);
|
||||
solve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to run the main logic
|
||||
*/
|
||||
public int[] solve() {
|
||||
setupAnts();
|
||||
clearTrails();
|
||||
IntStream.range(0, maxIterations).forEach(i -> {
|
||||
moveAnts();
|
||||
updateTrails();
|
||||
updateBest();
|
||||
});
|
||||
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() {
|
||||
IntStream.range(0, numberOfAnts).forEach(i -> {
|
||||
ants.forEach(ant -> {
|
||||
ant.clear();
|
||||
ant.visitCity(-1, random.nextInt(numberOfCities));
|
||||
});
|
||||
});
|
||||
currentIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* At each iteration, move ants
|
||||
*/
|
||||
private void moveAnts() {
|
||||
IntStream.range(currentIndex, numberOfCities - 1).forEach(i -> {
|
||||
ants.forEach(ant -> ant.visitCity(currentIndex, selectNextCity(ant)));
|
||||
currentIndex++;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Select next city for each ant
|
||||
*/
|
||||
private int selectNextCity(Ant ant) {
|
||||
int t = random.nextInt(numberOfCities - currentIndex);
|
||||
if (random.nextDouble() < randomFactor) {
|
||||
IntStream.range(0, numberOfCities).filter(i -> i == t && !ant.visited(i)).findFirst(); //TODO unused
|
||||
}
|
||||
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
|
||||
*/
|
||||
public 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.get(0).trail;
|
||||
bestTourLength = ants.get(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() {
|
||||
IntStream.range(0, numberOfCities).forEach(i -> {
|
||||
IntStream.range(0, numberOfCities).forEach(j -> trails[i][j] = c);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.baeldung.primitiveconversions;
|
||||
package com.baeldung;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
|
Loading…
Reference in New Issue