/* * Artificial Intelligence for Humans * Volume 1: Fundamental Algorithms * Java Version * http://www.aifh.org * http://www.jeffheaton.com * * Code repository: * https://github.com/jeffheaton/aifh * Copyright 2013 by Jeff Heaton * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * For more information on Heaton Research copyrights, licenses * and trademarks visit: * http://www.heatonresearch.com/copyright */ package com.heatonresearch.aifh.examples.discrete; import com.heatonresearch.aifh.discrete.DiscreteAnneal; import com.heatonresearch.aifh.distance.CalculateDistance; import com.heatonresearch.aifh.distance.EuclideanDistance; import com.heatonresearch.aifh.randomize.GenerateRandom; import com.heatonresearch.aifh.randomize.MersenneTwisterGenerateRandom; import java.util.Arrays; /** * Use simulated annealing with the Traveling Salesman Problem (TSP). The cities are placed in a circle, so the * ideal path is known. Because the cities are in a circle they should be visited in order for the absolute * optimal path. * <p/> * http://en.wikipedia.org/wiki/Traveling_salesman_problem */ public class TravelingSalesmanAnneal extends DiscreteAnneal { /** * The size of the map. */ public static final double MAP_SIZE = 10; /** * The city count. */ public static final int CITY_COUNT = 50; /** * The distance calculator. */ private final CalculateDistance distance = new EuclideanDistance(); /** * The city coordinates. */ private double[][] cities; /** * A random number generator. */ private final GenerateRandom rnd = new MersenneTwisterGenerateRandom(); /** * The current path being evaluated. */ private int[] currentPath; /** * The backup path, in case the current is not kept. */ private int[] backupPath; /** * The best path yet. */ private int[] bestPath; /** * Construct the object. */ public TravelingSalesmanAnneal() { super(1000, 400, 0.001); } /** * Run the example. */ public void run() { this.cities = new double[CITY_COUNT][2]; this.currentPath = new int[CITY_COUNT]; this.backupPath = new int[CITY_COUNT]; this.bestPath = new int[CITY_COUNT]; // place the cities in a circle final double ratio = (2 * Math.PI) / this.cities.length; for (int cityNumber = 0; cityNumber < cities.length; cityNumber++) { this.cities[cityNumber][0] = (int) (Math.cos(ratio * cityNumber) * (MAP_SIZE / 2) + (MAP_SIZE / 2)); this.cities[cityNumber][1] = (int) (Math.sin(ratio * cityNumber) * (MAP_SIZE / 2) + (MAP_SIZE / 2)); } // pick a random city order this.currentPath = new int[CITY_COUNT]; for (int i = 0; i < this.currentPath.length; i++) { int city; boolean foundCity; do { city = this.rnd.nextInt(CITY_COUNT); foundCity = false; for (int j = 0; j < i; j++) { if (city == this.currentPath[j]) { foundCity = true; } } } while (foundCity); this.currentPath[i] = city; } // now begin main loop, and find a minimum while (!done()) { this.iteration(); System.out.println("Iteration #" + getK() + ", Best Score=" + this.getBestScore() + "," + getStatus()); } System.out.println(Arrays.toString(this.bestPath)); } /** * {@inheritDoc} */ @Override public void backupState() { System.arraycopy(this.currentPath, 0, this.backupPath, 0, this.currentPath.length); } /** * {@inheritDoc} */ @Override public void restoreState() { System.arraycopy(this.backupPath, 0, this.currentPath, 0, this.currentPath.length); } /** * {@inheritDoc} */ @Override public void foundNewBest() { System.arraycopy(this.currentPath, 0, this.bestPath, 0, this.currentPath.length); } /** * {@inheritDoc} */ @Override public void moveToNeighbor() { // pick the first point to swap final int pt1 = this.rnd.nextInt(this.currentPath.length); // pick the second point to swap, can't be the same as the first int pt2; do { pt2 = this.rnd.nextInt(this.currentPath.length); } while (pt1 == pt2); // swap them final int temp = this.currentPath[pt1]; this.currentPath[pt1] = this.currentPath[pt2]; this.currentPath[pt2] = temp; } /** * {@inheritDoc} */ @Override public double evaluate() { double result = 0; for (int i = 0; i < (cities.length - 1); i++) { // find current and next city final double[] city1 = this.cities[this.currentPath[i]]; final double[] city2 = this.cities[this.currentPath[i + 1]]; result += this.distance.calculate(city1, city2); } return result; } /** * The main function. * * @param args Not used. */ public static void main(final String[] args) { final TravelingSalesmanAnneal prg = new TravelingSalesmanAnneal(); prg.run(); } }