要求:

TSP 算法(Traveling Salesman Problem)是指给定 n 个城市和各个城市之间的距离,要

求确定一条经过各个城市当且仅当一次的最短路径,它是一种典型的优化组合问题,其最优

解得求解代价是指数级的。TSP 问题代表一类优化组合问题,在实际工程中有很多应用,如

计算机联网、电子地图、交通诱导等,具有重要的研究价值。遗传算法和禁忌搜所算法都是

是一种智能优化算法,具有全局的优化性能、通用性强。这种算法一般具有严密的理论依据,

理论上可以在一定的时间内找到最优解或近似最优解,广泛应用于计算机科学优化调度问题、

组合优化问题。通过阅读书籍以及科技文献,研究遗传算法或 禁忌搜索算法的基本原理,

研究TSP 问题并提出常见解决方案。要求在此基础上,编程实现以智能优化算法来解决 TSP

问题,并给出相应实验结果和算法分析。

 

遗传算法:

package org.wucl.ga;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;import org.wucl.City;
/**
 * 遗传算法解决TSP问题
 * 
 * 1.个体编码方案 整数编码:对城市进行编号,每个城市分别用0到n-1之间不同的整数表示,n个整数的一个排列就代表了旅行商问题的一个可能解
 * 
 * 2.交配方法 常规交配法,交配概率为100%
 * 
 * 3.变异方法 打乱变异,使用逆序方式,变异概率为10%
 * 
 * 4.新种群构成的方法 用轮盘赌求出子代种群
 * 
 * 5.算法结束的条件 当代数打到200时,结束算法
 * 
 * 
 * 
 */
public class Genetic { List<City> vec = new ArrayList<City>();
 int n = 0;
 int num;
 double optimal; // 记录最优解
 int best[] = new int[n]; // 记录最终路径 public void getStrategy() {
 try {
 Reader reader = new InputStreamReader(Genetic.class
 .getClassLoader().getResourceAsStream("data.txt"));
 BufferedReader br = new BufferedReader(reader);
 String line = br.readLine();
 while (line != null) {
 String[] sa = line.split(" ");
 vec.add(new City(sa[0], Double.parseDouble(sa[1]), Double
 .parseDouble(sa[2])));
 line = br.readLine();
 }
 n = vec.size();
 } catch (IOException e) {
 e.printStackTrace();
 }
 num = 4 * n * n; // 初始个体数为4*n*n;
 int a[][] = new int[num][n]; for (int i = 0; i < num; i++) // 随机生成num个个体
 {
 a[i] = random(n, n);
 }
 optimal = getValue(a[0])[1];
 for (int i = 0; i < 200; i++) {
 a = nextGen(a);
 }
 System.out.print("最佳路径:");
 for (int i = 0; i < n - 1; i++)
 System.out.print(vec.get(best[i]).name + "->");
 System.out.println(vec.get(best[n - 1]).name);
 System.out.println("最佳长度:" + optimal);
 } public int[] random(int m, int t) // 生成互不相同的随机数序列的函数
 {
 Random r = new Random();
 int k = 0;
 int tmp[] = new int[m];
 for (int i = 0; i < m; i++) {
 while (true) {
 boolean flag = true;
 tmp[i] = r.nextInt(t);
 k++;
 for (int j = 0; j < k - 1; j++) {
 if (tmp[i] == tmp[j]) {
 k--;
 flag = false;
 break;
 }
 }
 if (flag == true)
 break;
 }
 }
 return tmp;
 } public int[][] Crossove(int a[][]) // 交配函数
 {
 int A[][] = new int[num][n];
 int k = 0;
 for (int i = 0; i < num; i = i + 2) {
 Random r = new Random();
 int loc = r.nextInt(n - 1);
 for (int j = 0; j <= loc; j++) {
 A[k][j] = a[i][j];
 A[k + 1][j] = a[i + 1][j];
 }
 int m1 = loc + 1, m2 = m1;
 for (int t = 0; t < n; t++) {
 if (!is_exist(loc, a[i + 1][t], a[i])) {
 A[k][m1] = a[i + 1][t];
 m1++;
 }
 if (!is_exist(loc, a[i][t], a[i + 1])) {
 A[k + 1][m2] = a[i][t];
 m2++;
 } }
 if (r.nextInt(100) < 10) {
 A[k] = variation(A[k]);
 }
 if (r.nextInt(100) < 10) {
 A[k + 1] = variation(A[k + 1]);
 }
 k += 2;
 }
 return A;
 } public int[][] nextGen(int a[][]) // 生成新种群的函数
 {
 int A[][] = new int[num][n];
 double p[] = new double[num];
 double value[] = new double[num];
 double sum = 0;
 for (int i = 0; i < num; i++) {
 double tmp[] = new double[2];
 tmp = getValue(a[i]);
 value[i] = tmp[0];
 if (optimal > tmp[1]) {
 optimal = tmp[1];
 best = a[i];
 }
 sum += value[i];
 }
 for (int i = 0; i < num; i++) {
 p[i] = value[i] / sum;
 }
 int k = 0;
 for (int i = 0; i < num; i++) {
 double s = 0;
 int j = 0;
 double r = Math.random();
 while (true) {
 if (s >= r)
 break;
 s += p[j];
 j++;
 }
 A[k] = a[j - 1];
 k++;
 }
 return Crossove(A);
 } public boolean is_exist(int loc, int x, int b[]) {
 for (int i = 0; i <= loc; i++) {
 if (x == b[i])
 return true;
 }
 return false;
 } public int[] variation(int a[]) // 变异函数
 {
 int b[] = new int[n];
 Random r = new Random();
 int x = r.nextInt(n);
 int y = r.nextInt(n);
 while (y == x)
 y = r.nextInt(n);
 if (x < y) {
 int k = 0;
 for (int i = 0; i < n; i++) {
 if (i < x)
 b[i] = a[i];
 else if (i > y)
 b[i] = a[i];
 else {
 b[i] = a[y - k];
 k++;
 }
 }
 } else {
 int yy = y, k = 1;
 for (int i = 0; i < n; i++) {
 if (i <= yy) {
 b[i] = a[y];
 y--;
 } else if (i >= x) {
 b[i] = a[n - k];
 k++;
 } else
 b[i] = a[i]; }
 }
 return b;
 } public double[] getValue(int a[]) // 获取路径值的函数
 {
 double length = 0;
 int k = a.length - 1;
 for (int i = 0; i < k; i++) {
 double tmp1 = Math.pow(vec.get(a[i]).x - vec.get(a[i + 1]).x, 2);
 double tmp2 = Math.pow(vec.get(a[i]).y - vec.get(a[i + 1]).y, 2);
 length += Math.sqrt(tmp1 + tmp2);
 }
 double tmp1 = Math.pow(vec.get(a[0]).x - vec.get(a[k]).x, 2);
 double tmp2 = Math.pow(vec.get(a[0]).y - vec.get(a[k]).y, 2);
 length += Math.sqrt(tmp1 + tmp2);
 double f[] = new double[2];
 f[0] = 1 / Math.pow(length, 8);
 f[1] = length;
 return f;
 } public static void main(String argv[]) {
 new Genetic().getStrategy();
 }
}

 

 

 

禁忌搜索算法(1):单线程实现

package org.wucl.ts;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Random;import org.wucl.ga.Genetic;
/**
 * 单线程实现禁忌搜索算法解决TSP问题
 * 
 * 
 * 
 *
 */
public class Tabu { private int MAX_GEN;// 迭代次数
 private int N;// 每次搜索邻居个数
 private int ll;// 禁忌长度
 private int cityNum; // 城市数量,编码长度
 private double[][] distance; // 距离矩阵
 private int bestT;// 最佳出现代数
 private int[] Ghh;// 初始路径编码
 private int[] bestGh;// 最好的路径编码
 private double bestEvaluation;
 private int[] LocalGhh;// 当代最好编码
 private double localEvaluation;
 private int[] tempGhh;// 存放临时编码
 private double tempEvaluation;
 private int[][] jinji;// 禁忌表
 private int t;// 当前代数
 private Random random;
 public Tabu() {
 }
 /**
 * 
 * constructor of Tabu
 * 
 * @param n
 * 
 * 城市数量
 * @param g
 * 
 * 运行代数
 * @param c
 * 
 * 每次搜索邻居个数
 * 
 * @param m
 * 
 * 禁忌长度
 * 
 **/ public Tabu(int n, int g, int c, int m) {
 cityNum = n;
 MAX_GEN = g;
 N = c;
 ll = m;
 } /**
 * 初始化Tabu算法类
 * @param filename 数据文件名,该文件存储所有城市节点坐标数据
 * @throws IOException
 */
 private void init() throws IOException { // 读取数据
 int[] x;
 int[] y;
 String strbuff;
 Reader reader = new InputStreamReader(Genetic.class.getClassLoader().getResourceAsStream("data.txt"));
 BufferedReader data = new BufferedReader(reader);
 distance = new double[cityNum][cityNum];
 x = new int[cityNum];
 y = new int[cityNum];
 for (int i = 0; i < cityNum; i++) {
 strbuff = data.readLine();
 String[] strcol = strbuff.split(" ");
 x[i] = Integer.valueOf(strcol[1]);// x坐标
 y[i] = Integer.valueOf(strcol[2]);// y坐标 }
 // 计算距离矩阵
 //System.out.println("距离矩阵为:");
 for (int i = 0; i < cityNum - 1; i++) {
 distance[i][i] = 0; // 对角线为0
 for (int j = 0; j < cityNum; j++) {
 distance[i][j] = Math
 .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
 * (y[i] - y[j])));
 //System.out.print(Math.ceil(distance[i][j]) + " ");
 }
 //System.out.println(); }
 distance[cityNum - 1][cityNum - 1] = 0;
 Ghh = new int[cityNum];
 bestGh = new int[cityNum];
 bestEvaluation = Integer.MAX_VALUE;
 LocalGhh = new int[cityNum];
 localEvaluation = Integer.MAX_VALUE;
 tempGhh = new int[cityNum];
 tempEvaluation = Integer.MAX_VALUE;
 jinji = new int[ll][cityNum];
 bestT = 0;
 t = 0;
 random = new Random(System.currentTimeMillis());
 } // 初始化编码Ghh
 void initGroup() {
 int i, j;
 Ghh[0] = random.nextInt(65535) % cityNum;
 for (i = 1; i < cityNum;)// 编码长度
 {
 Ghh[i] = random.nextInt(65535) % cityNum;
 for (j = 0; j < i; j++) {
 if (Ghh[i] == Ghh[j]) {
 break;
 }
 }
 if (j == i) {
 i++;
 }
 }
 } // 复制编码体,复制编码Gha到Ghb
 public void copyGh(int[] Gha, int[] Ghb) {
 for (int i = 0; i < cityNum; i++) {
 Ghb[i] = Gha[i];
 }
 } public double evaluate(int[] chr) {
 double len = 0;
 // 编码,起始城市,城市1,城市2...城市n
 for (int i = 1; i < cityNum; i++) {
 len += distance[chr[i - 1]][chr[i]];
 }
 // // 城市n,起始城市
 len += distance[chr[cityNum - 1]][chr[0]];
 // for (int i = 1; i < cityNum; i++) {
 // System.out.print(chr[i] + ",");
 // }
 // System.out.println("-------------" + len);
 return len;
 } // 邻域交换,得到邻居
 public void Linju(int[] Gh, int[] tempGh) {
 int i, temp;
 int ran1, ran2;
 for (i = 0; i < cityNum; i++) {
 tempGh[i] = Gh[i];
 }
 ran1 = random.nextInt(65535) % cityNum;
 ran2 = random.nextInt(65535) % cityNum;
 while (ran1 == ran2) {
 ran2 = random.nextInt(65535) % cityNum;
 }
 temp = tempGh[ran1];
 tempGh[ran1] = tempGh[ran2];
 tempGh[ran2] = temp; }
 // 判断编码是否在禁忌表中
 public int panduan(int[] tempGh) {
 int i, j;
 int flag = 0;
 for (i = 0; i < ll; i++) {
 flag = 0;
 for (j = 0; j < cityNum; j++) {
 if (tempGh[j] != jinji[i][j]) {
 flag = 1;// 不相同
 break;
 }
 }
 if (flag == 0)// 相同,返回存在相同
 {
 // return 1;
 break; }
 } if (i == ll)// 不等
 {
 return 0;// 不存在 } else {
 return 1;// 存在
 }
 } // 解禁忌与加入禁忌
 public void jiejinji(int[] tempGh) {
 int i, j, k;
 // 删除禁忌表第一个编码,后面编码往前挪动
 for (i = 0; i < ll - 1; i++) {
 for (j = 0; j < cityNum; j++) {
 jinji[i][j] = jinji[i + 1][j];
 }
 } // 新的编码加入禁忌表
 for (k = 0; k < cityNum; k++) {
 jinji[ll - 1][k] = tempGh[k];
 }
 } public void solve() {
 int nn;
 // 初始化编码Ghh
 initGroup();
 copyGh(Ghh, bestGh);// 复制当前编码Ghh到最好编码bestGh
 bestEvaluation = evaluate(Ghh);
 while (t < MAX_GEN) {
 nn = 0;
 localEvaluation = Integer.MAX_VALUE;
 while (nn < N) {
 Linju(Ghh, tempGhh);// 得到当前编码Ghh的邻域编码tempGhh
 if (panduan(tempGhh) == 0)// 判断编码是否在禁忌表中
 {
 // 不在
 tempEvaluation = evaluate(tempGhh);
 if (tempEvaluation < localEvaluation) {
 copyGh(tempGhh, LocalGhh);
 localEvaluation = tempEvaluation;
 }
 nn++;
 }
 }
 if (localEvaluation < bestEvaluation) {
 bestT = t;
 copyGh(LocalGhh, bestGh);
 bestEvaluation = localEvaluation;
 }
 copyGh(LocalGhh, Ghh);
 // 解禁忌表,LocalGhh加入禁忌表
 jiejinji(LocalGhh);
 t++;
 }
 System.out.print("最佳长度出现代数:");
 System.out.println(bestT);
 System.out.print("最佳长度:");
 System.out.println(bestEvaluation);
 System.out.print("最佳路径:");
 for (int i = 0; i < cityNum; i++) {
 System.out.print(bestGh[i] + "->");
 }
 } public static void main(String[] args) throws IOException {
 System.out.println("Start....");
 Tabu tabu = new Tabu(30,200000, 200, 20);
 tabu.init();
 long t1 = System.currentTimeMillis();
 tabu.solve();
 System.out.println();
 System.out.println("计算用时:" + (System.currentTimeMillis() - t1));
 }}

 

禁忌搜索算法(2):多线程是实现

package org.wucl.ts;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Random;import org.wucl.ga.Genetic;

 public static int MAX_GEN;// 迭代次数
 private int N;// 每次搜索邻居个数
 private int ll;// 禁忌长度
 private int cityNum; // 城市数量,编码长度
 private double[][] distance; // 距离矩阵
 public int bestT;// 最佳出现代数
 private int[] Ghh;// 初始路径编码
 public int[] bestGh;// 最好的路径编码
 public double bestEvaluation;
 private int[] LocalGhh;// 当代最好编码
 public double localEvaluation;
 private int[] tempGhh;// 存放临时编码
 private double tempEvaluation;
 private static int[][] jinji;// 禁忌表
 private static int t;// 当前代数
 private Random random;
 private Object obj1 = new Object();
 private Object obj2 = new Object();
 public Tabu2() {
 }
 /**
 * 
 * constructor of Tabu
 * 
 * @param n
 * 
 * 城市数量
 * @param g
 * 
 * 运行代数
 * @param c
 * 
 * 每次搜索邻居个数
 * 
 * @param m
 * 
 * 禁忌长度
 * 
 **/ public Tabu2(int n, int c, int m) {
 cityNum = n;
 N = c;
 ll = m;
 } /**
 * 初始化Tabu算法类
 * @param filename 数据文件名,该文件存储所有城市节点坐标数据
 * @throws IOException
 */
 private void init() throws IOException { // 读取数据
 int[] x;
 int[] y;
 String strbuff;
 Reader reader = new InputStreamReader(Genetic.class.getClassLoader()
 .getResourceAsStream("data.txt"));
 BufferedReader data = new BufferedReader(reader);
 distance = new double[cityNum][cityNum];
 x = new int[cityNum];
 y = new int[cityNum];
 for (int i = 0; i < cityNum; i++) {
 strbuff = data.readLine();
 String[] strcol = strbuff.split(" ");
 x[i] = Integer.valueOf(strcol[1]);// x坐标
 y[i] = Integer.valueOf(strcol[2]);// y坐标 }
 // 计算距离矩阵
 // System.out.println("距离矩阵为:");
 for (int i = 0; i < cityNum - 1; i++) {
 distance[i][i] = 0; // 对角线为0
 for (int j = 0; j < cityNum; j++) {
 distance[i][j] = Math
 .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
 * (y[i] - y[j])));
 } }
 distance[cityNum - 1][cityNum - 1] = 0;
 Ghh = new int[cityNum];
 bestGh = new int[cityNum];
 bestEvaluation = Integer.MAX_VALUE;
 LocalGhh = new int[cityNum];
 localEvaluation = Integer.MAX_VALUE;
 tempGhh = new int[cityNum];
 tempEvaluation = Integer.MAX_VALUE;
 jinji = new int[ll][cityNum];
 bestT = 0;
 t = 0;
 random = new Random(System.currentTimeMillis());
 } // 初始化编码Ghh
 void initGroup() {
 int i, j;
 Ghh[0] = random.nextInt(65535) % cityNum;
 for (i = 1; i < cityNum;)// 编码长度
 {
 Ghh[i] = random.nextInt(65535) % cityNum;
 for (j = 0; j < i; j++) {
 if (Ghh[i] == Ghh[j]) {
 break;
 }
 }
 if (j == i) {
 i++;
 }
 }
 } // 复制编码体,复制编码Gha到Ghb
 public void copyGh(int[] Gha, int[] Ghb) {
 for (int i = 0; i < cityNum; i++) {
 Ghb[i] = Gha[i];
 }
 } public double evaluate(int[] chr) {
 double len = 0;
 // 编码,起始城市,城市1,城市2...城市n
 for (int i = 1; i < cityNum; i++) {
 len += distance[chr[i - 1]][chr[i]];
 }
 // // 城市n,起始城市
 len += distance[chr[cityNum - 1]][chr[0]];
 return len;
 } // 邻域交换,得到邻居
 public void Linju(int[] Gh, int[] tempGh) {
 int i, temp;
 int ran1, ran2;
 for (i = 0; i < cityNum; i++) {
 tempGh[i] = Gh[i];
 }
 ran1 = random.nextInt(65535) % cityNum;
 ran2 = random.nextInt(65535) % cityNum;
 while (ran1 == ran2) {
 ran2 = random.nextInt(65535) % cityNum;
 }
 temp = tempGh[ran1];
 tempGh[ran1] = tempGh[ran2];
 tempGh[ran2] = temp; }
 // 判断编码是否在禁忌表中
 public int panduan(int[] tempGh) {
 int i, j;
 int flag = 0;
 for (i = 0; i < ll; i++) {
 flag = 0;
 for (j = 0; j < cityNum; j++) {
 if (tempGh[j] != jinji[i][j]) {
 flag = 1;// 不相同
 break;
 }
 }
 if (flag == 0)// 相同,返回存在相同
 {
 // return 1;
 break; }
 } if (i == ll)// 不等
 {
 return 0;// 不存在
 } else {
 return 1;// 存在
 }
 } // 解禁忌与加入禁忌
 public void jiejinji(int[] tempGh) {
 synchronized (obj2) {
 int i, j, k;
 // 删除禁忌表第一个编码,后面编码往前挪动
 for (i = 0; i < ll - 1; i++) {
 for (j = 0; j < cityNum; j++) {
 jinji[i][j] = jinji[i + 1][j];
 }
 } // 新的编码加入禁忌表
 for (k = 0; k < cityNum; k++) {
 jinji[ll - 1][k] = tempGh[k];
 }
 }
 } public void solve() {
 try {
 init();
 } catch (IOException e) {
 e.printStackTrace();
 }
 int nn;
 // 初始化编码Ghh
 initGroup();
 copyGh(Ghh, bestGh);// 复制当前编码Ghh到最好编码bestGh
 bestEvaluation = evaluate(Ghh);
 while (t < MAX_GEN) {
 nn = 0;
 localEvaluation = Integer.MAX_VALUE;
 while (nn < N) {
 Linju(Ghh, tempGhh);// 得到当前编码Ghh的邻域编码tempGhh
 if (panduan(tempGhh) == 0)// 判断编码是否在禁忌表中
 {
 // 不在
 tempEvaluation = evaluate(tempGhh);
 if (tempEvaluation < localEvaluation) {
 copyGh(tempGhh, LocalGhh);
 localEvaluation = tempEvaluation;
 }
 nn++;
 }
 }
 if (localEvaluation < bestEvaluation) {
 bestT = t;
 copyGh(LocalGhh, bestGh);
 bestEvaluation = localEvaluation;
 }
 copyGh(LocalGhh, Ghh);
 // 解禁忌表,LocalGhh加入禁忌表
 jiejinji(LocalGhh);
 synchronized (obj1) {
 t++;
 }
 }
 } public static void main(String[] args) {
 Tabu2.MAX_GEN = 200000;
 final Tabu2 tabu1 = new Tabu2(30, 200, 20);
 final Tabu2 tabu2 = new Tabu2(30, 200, 20);
 final Thread thread1 = new Thread("thread-1") {
 @Override
 public void run() {
 System.out.println("thread-1 start...");
 tabu1.solve();
 }
 };
 final Thread thread2 = new Thread("thread-2") {
 @Override
 public void run() {
 System.out.println("thread-2 start...");
 tabu2.solve();
 }
 };
 final long t1 = System.currentTimeMillis();
 Thread thread3 = new Thread("thread-2") {
 @Override
 public void run() {
 System.out.println("thread-3 start...");
 while (true) {
 if (!thread1.isAlive() && !thread2.isAlive()) {
 Tabu2 bestTabu = tabu1;
 if (tabu1.localEvaluation <= tabu2.localEvaluation) {
 bestTabu = tabu2;
 }
 System.out.print("最佳长度出现代数:");
 System.out.println(bestTabu.bestT);
 System.out.print("最佳长度:");
 System.out.println(bestTabu.bestEvaluation);
 System.out.print("最佳路径:");
 for (int i = 0; i < 30; i++) {
 System.out.print(bestTabu.bestGh[i] + "->");
 }
 System.out.println();
 break;
 }
 }
 System.out.println("计算用时:" + (System.currentTimeMillis() - t1));
 }
 };
 thread1.start();
 thread2.start();
 thread3.start();
 }}