要求:

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时,结束算法
 * 
 * @author wucl(lailaiwcl@163.com)
 * 
 */
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问题
 * 
 * 
 * @author wucl(lailaiwcl@163.com)
 *
 */
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;
/**
 * 多线程调度实现禁忌搜索算法解决TSP问题
 * 
 * 
 * @author wucl(lailaiwcl@163.com)
 * 
 */
public class Tabu2 {	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();
	}}

 

大江淘尽,各数风流!