Package

Source Code of TravelingSalesman

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

/**
* This class implements the Traveling Salesman problem as a Java applet.
*/

public class TravelingSalesman extends Applet implements Runnable {

  private static final Random RANDOM = new Random();

  /**
   * true: view the algorithm run once. false: run many times and write data.
   */
  private boolean gettingData = false;

  private static final long serialVersionUID = 1731368983441078792L;

  /**
   * How many cities to use.
   */
  protected int cityCount;

  /**
   * How many chromosomes to use.
   */
  protected int populationSize;

  /**
   * The part of the population eligible for mating.
   */
  protected int matingPopulationSize;

  /**
   * The part of the population selected for mating.
   */
  protected int selectedParents;

  /**
   * The current generation
   */
  protected int generation;

  /**
   * The background worker thread.
   */
  protected Thread worker = null;

  /**
   * Is the thread started.
   */
  protected boolean started = false;

  /**
   * The list of cities.
   */
  protected City[] cities;

  /**
   * The list of chromosomes being displayed
   */
  protected Chromosome[] chromosomes;

  /** The number of populations the chromosomes are initially divided into */
  private static final int SUB_POPULATIONS = 10;

  /**
   * The subpopulations. After 600 generations this reduces to a single
   * population (an array containing one array only).
   */
  private Chromosome[][] populations;

  /**
   * The Start button.
   */
  private Button ctrlStart;

  /**
   * The TextField that holds the number of cities.
   */
  private TextField ctrlCities;

  /**
   * The TextField for the population size.
   */
  private TextField ctrlPopulationSize;

  /**
   * Holds the buttons and other controls, forms a strip across the bottom of
   * the applet.
   */
  private Panel ctrlButtons;

  /**
   * The current status, which is displayed just above the controls.
   */
  private String status = "";

  @Override
  public void init() {
    setLayout(new BorderLayout());

    // setup the controls
    ctrlButtons = new Panel();
    ctrlStart = new Button("Start");
    ctrlButtons.add(ctrlStart);
    ctrlButtons.add(new Label("# Cities:"));
    ctrlButtons.add(ctrlCities = new TextField(5));
    ctrlButtons.add(new Label("Population Size:"));
    ctrlButtons.add(ctrlPopulationSize = new TextField(5));
    this.add(ctrlButtons, BorderLayout.SOUTH);

    // set the default values
    ctrlPopulationSize.setText("1000");
    ctrlCities.setText("100");

    // add an action listener for the button
    ctrlStart.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent arg0) {
        if (gettingData) {
          getData();
        } else {
          startThread();
        }
      }

    });

    started = false;
    update();
  }

  private void log(String s) {
    if (printer != null) {
      printer.print(s);
    }
    System.out.print(s);
  }

  /** Runs the algorithm multiple times and writes the results to a file */
  private void getData() {
    File file = new File("data_100.csv");
    boolean overwrite = false;
    if (file.exists() && !overwrite) {
      throw new IllegalArgumentException(
          "File already exists: change name to avoid data loss or set overwrite = true");
    }
    try {
      printer = new PrintWriter(file);
      int runs = 50;
      double sum = 0;
      double min = Double.POSITIVE_INFINITY;
      double max = Double.NEGATIVE_INFINITY;
      double costs[] = new double[runs];
      for (int run = 0; run < runs; run++) {
        log("Run, " + run + "\n");
        startThread();
        try {

          // Wait for the current run to finish
          worker.join();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        double cost = chromosomes[0].getCost();
        sum += cost;
        min = Math.min(min, cost);
        max = Math.max(max, cost);
        costs[run] = cost;
      }
      double mean = sum / runs;
      double variance = 0;
      for (double cost : costs) {
        variance += (mean - cost) * (mean - cost);
      }
      variance /= runs;
      log("Mean, " + mean + "\n");
      log("Min, " + min + "\n");
      log("Max, " + max + "\n");
      log("Variance, " + variance + "\n");
      log("Cities, " + cityCount + "\n");

      // Notify completion
      for (int x = 0; x < 10; x++) {
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Toolkit.getDefaultToolkit().beep();
      }

      printer.close();
    } catch (FileNotFoundException e1) {
      e1.printStackTrace();
    }
  }

  /**
   * Start the background thread.
   */
  public void startThread() {

    try {
      cityCount = Integer.parseInt(ctrlCities.getText());
    } catch (NumberFormatException e) {
      cityCount = 200;
    }

    try {
      populationSize = Integer.parseInt(ctrlPopulationSize.getText());
    } catch (NumberFormatException e) {
      populationSize = 1000;
    }

    FontMetrics fm = getGraphics().getFontMetrics();
    int bottom = ctrlButtons.getBounds().y - fm.getHeight() - 2;

    // Create a random list of cities
    cities = new City[cityCount];
    for (int i = 0; i < cityCount; i++) {
      cities[i] = new City(
          (int) (Math.random() * (getBounds().width - 10)),
          (int) (Math.random() * (bottom - 10)));
    }

    // Create subpopulations
    populations = new Chromosome[SUB_POPULATIONS][populationSize
        / SUB_POPULATIONS];
    for (Chromosome[] population : populations) {
      for (int i = 0; i < population.length; i++) {
        population[i] = new Chromosome(cities);
      }
    }

    // Display the first subpopulation in the GUI
    chromosomes = populations[0];

    // Start up the background thread
    generation = 0;
    worker = new Thread(this);
    worker.setPriority(Thread.MIN_PRIORITY);
    worker.start();
    started = true;
  }

  /**
   * Update the display
   */
  public void update() {
    Image img = createImage(getBounds().width, getBounds().height);
    Graphics g = img.getGraphics();
    FontMetrics fm = g.getFontMetrics();

    int width = getBounds().width;
    int bottom = ctrlButtons.getBounds().y - fm.getHeight() - 2;

    g.setColor(Color.black);
    g.fillRect(0, 0, width, bottom);

    if (started && (cities != null)) {
      g.setColor(Color.green);
      for (int i = 0; i < cityCount; i++) {
        int xpos = cities[i].getx();
        int ypos = cities[i].gety();
        g.fillOval(xpos - 5, ypos - 5, 10, 10);
      }

      g.setColor(Color.white);
      for (int i = 0; i < cityCount; i++) {
        int icity = chromosomes[0].getCity(i);
        if (i != 0) {
          int last = chromosomes[0].getCity(i - 1);
          g.drawLine(cities[icity].getx(), cities[icity].gety(),
              cities[last].getx(), cities[last].gety());
        }
      }

    }

    g.drawString(status, 0, bottom);

    getGraphics().drawImage(img, 0, 0, this);
  }

  /**
   * Update the status.
   *
   * @param status
   *            The status.
   */
  public void setStatus(String status) {
    this.status = status;
  }

  /**
   * The main loop for the background thread.
   */
  @Override
  public void run() {

    if (!gettingData) {
      update();
    }

    // For printing. Note that every run will print a row for every
    // subpopulation, showing the costs in parallel. The first row will
    // contain the final results of the whole population at the end.
    List<ArrayList<Integer>> costs = new ArrayList<>(SUB_POPULATIONS);
    for (Chromosome[] population : populations) {
      sortChromosomes(population);
      costs.add(new ArrayList<Integer>());
    }

    while (generation < 1000) {

      generation++;

      if (generation == 600) {
        Chromosome[][] populationsNew = new Chromosome[1][populations[0].length
            * SUB_POPULATIONS];
        int i = 0;
        for (Chromosome[] population : populations) {
          for (Chromosome chromosome : population) {
            populationsNew[0][i++] = chromosome;
          }
        }
        populations = populationsNew;
        sortChromosomes(populations[0]);
        chromosomes = populations[0];
      }

      for (Chromosome[] population : populations) {
        tournament(population);
        sortChromosomes(population);
      }

      for (int i = 0; i < populations.length; i++) {
        costs.get(i).add((int) populations[i][0].getCost());
      }

      // Chromosome.sortChromosomes(chromosomes, matingPopulationSize);
      if (!gettingData) {
        setStatus("Generation " + generation + " Cost "
            + (int) chromosomes[0].getCost());
        update();
      }
    }
    setStatus("Solution found after " + generation + " generations.");

    // Print out costs at each generation for all the subpopulations (see
    // above)
    for (ArrayList<Integer> costList : costs) {
      log(costList.get(0) + "");
      for (int i = 1; i < costList.size(); i++) {
        log("," + costList.get(i));
      }
      log("\n");
    }
  }

  /**
   * Selects random pairs of chromosomes to recombine and compete for the
   * chance to produce mutants
   */
  private void tournament(Chromosome[] population) {

    // Create a list of shuffled positions
    List<Integer> positions = new ArrayList<>(population.length);
    for (int i = 0; i < population.length; i++) {
      positions.add(i);
    }
    Collections.shuffle(positions);

    // Select pairs, produce a child, and mutate the winner
    for (int i = 0; i < population.length; i += 2) {
      Integer pos1 = positions.get(i);
      Integer pos2 = positions.get(i + 1);
      Chromosome c1 = population[pos1];
      Chromosome c2 = population[pos2];

      // Recombine
      Chromosome child = new Chromosome(c1, c2);
      child.calculateCost(cities);

      // Find the best of the three
      Chromosome winner = c1.getCost() < c2.getCost() ? c1 : c2;
      winner = winner.getCost() < child.getCost() ? winner : child;

      // Mutate the winner
      mutateInto(winner, pos1, population);
      mutateInto(winner, pos2, population);
    }
  }

  /**
   * Creates a child of the given chromosome and places it at position newpos.
   * A mutant is created and if that mutant is better than the parent, it is
   * the child. Otherwise the child is identical to the parent.
   */
  private void mutateInto(Chromosome original, int newpos,
      Chromosome[] population) {
    Chromosome mutant;
    Chromosome best = new Chromosome(original);
    mutant = new Chromosome(original);

    // Perform a random mutation
    int mutation = RANDOM.nextInt(4);
    switch (mutation) {
    case 0:
      mutant.invert();
      break;
    case 1:
      mutant.translocate();
      break;
    case 2:
      mutant.transpose();
      break;
    case 3:
      mutant.shift();
      break;
    }
    mutant.calculateCost(cities);

    // Only keep beneficial mutations
    if (mutant.getCost() < original.getCost()) {
      best = mutant;
    }
    population[newpos] = best;
  }

  /** Orders chromosomes by cost */
  private static final Comparator<Chromosome> COST_COMPARATOR = new Comparator<Chromosome>() {

    @Override
    public int compare(Chromosome c1, Chromosome c2) {
      return (int) Math.signum(c1.getCost() - c2.getCost());
    }
  };

  private PrintWriter printer;

  /** Calculate costs, then sort the entire array */
  private void sortChromosomes(Chromosome[] array) {
    for (Chromosome c : array) {
      c.calculateCost(cities);
    }
    Arrays.sort(array, COST_COMPARATOR);
  }

  @Override
  public void paint(Graphics g) {
    update();
  }
}
TOP

Related Classes of TravelingSalesman

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.