一、禁忌搜索算法步骤与流程
- 邻域移动:邻域移动是从一个解产生另一个解的途径。本文采用2-opt。
- 禁忌表:通常记录前若干次的移动,禁止这些移动在近期内返回。在迭代固定次数后,禁忌表释放这些移动,重新参加运算。
- 选择策略:即择优规则,是对当前的邻域移动选择而采用的准则。当前采用最广泛的两类策略是最好解优先策略和第一个改进解优先策略。本文采用最好解优先
- 特赦准则:考虑邻域内所有解(包括禁忌解),并逐一进行评估,发现出色的禁忌解,将其解禁。
- 终止规则:迭代次数控制。
算法流程图如下所示:
二、基于禁忌搜索的TSP问题建模求解(Java)
20个城市的TSP问题,每个城市的坐标如下(data.txt):
60,200 180,200 80,180 140,180 20,160 100,160 200,160 140,140 40,120 100,120 180,100 60,80 120,80 180,60 20,40 100,40 200,40 20,20 60,20 160,20
- Data读取文件类
- UtileMeth工具方法类
- TabuSearch算法类
- Test测试类
package tsp.tabusearch;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Data {
static int cityNum;
//读文件
public static int[][] readFile(String filename) {
int[][] xy = new int[cityNum][cityNum];
try {
BufferedReader bf = new BufferedReader(new FileReader(filename));
for (int i = 0; i < cityNum; i++) {
String[] coordinate = bf.readLine().split(",");
xy[i][0] = Integer.parseInt(coordinate[0]);
xy[0][i] = Integer.parseInt(coordinate[1]);
}
bf.close();
} catch (IOException e) {
e.printStackTrace();
}
int[][] distance = new int[cityNum][cityNum];
for (int i = 0; i < cityNum; i++) {
distance[i][i] = 0;
for (int j = 0; j < cityNum; j++) {
distance[i][j] = distance[j][i] = (int) Math.sqrt(Math.abs((xy[i][0] - xy[j][0])) * Math.abs((xy[i][0] - xy[j][0]))
+ Math.abs((xy[0][i] - xy[0][j])) * Math.abs((xy[i][0] - xy[j][0])));
}
}
return distance;
}
}
package tsp.tabusearch;
import java.util.HashSet;
import java.util.Iterator;
public class UtilMeth {
/**
* 产生指定范围的随机数组
* <p>
* HashSet + 递归
*
* @param min 指定范围最小值
* @param max 指定范围最大值
*/
//产生一个不重复的随机数组
public static int[] randArr(int min, int max) {
if (max < min) throw new IllegalArgumentException("error param!");
int[] randArr = new int[max - min];
HashSet<Integer> set = new HashSet<>();
gen(min, max, set);
// 如果存入的数小于指定生成的个数,则调用递归再生成剩余个数的随机数,如此循环,直到达到指定大小
while (set.size() < max - min)
gen(min, max, set);// 递归
Iterator<Integer> it = set.iterator();
int i = 0;
while (it.hasNext()) {
randArr[i] = it.next();
i++;
}
return randArr;
}
private static void gen(int min, int max, HashSet<Integer> set) {
for (int i = 0; i < max - min; i++) {
int num = (int) ((Math.random()) * (max - min)) + min;
set.add(num);// 将不同的数存入HashSet中
}
}
//数组复制
public static void reproduce(int[] route1, int[] route2) {
for (int i = 0; i < route2.length; i++)
route1[i] = route2[i];
}
//数组复制
public static int[] reproduce(int[] route) {
int[] route_copy = new int[route.length];
reproduce(route_copy, route);
return route_copy;
}
}
package tsp.tabusearch;
import static net.mindview.util.Print.*;
import java.util.Arrays;
import java.util.Random;
public class TabuSearch {
public int cityNum;
public int[] route;
int[] optimalRoute;
int tableLen;
int[][] tabuTable;
final int MAX_GEN = 1000;
final int UMG_SCOP = 200;
public Random rand;
TabuSearch(int cityNum) {
this.cityNum = cityNum;
Data.cityNum = cityNum;
route = new int[cityNum];
optimalRoute = new int[cityNum];
int tableLen = 20;
tabuTable = new int[tableLen][cityNum];
rand = new Random(System.currentTimeMillis());
}
//生成一个初始解
public void initGroup() {
route = UtilMeth.randArr(0, cityNum).clone();
print("初始路径:" + Arrays.toString(route));
print("初始距离:" + evaluation(route));
}
//评价函数
int evaluation(int[] route) {
int[][] dist = Data.readFile("D:\\Users\\36297\\JavaWorkspace\\algori\\src\\tsp\\geneticalgori\\data.txt");
int len = 0;
for (int i = 1; i < cityNum; i++)
len += dist[route[i - 1]][route[i]];
len += dist[route[cityNum - 1]][route[0]];
return len;
}
//邻域移动
void moveOperator(int[] route) {
int rand1 = rand.nextInt(20);
int rand2 = rand.nextInt(20);
int city1 = route[rand1];
int city2 = route[rand2];
route[rand2] = city1;
route[rand1] = city2;
}
//特赦准则(藐视准则、破禁准则)
boolean aspirationCriterion(int[] route) {
boolean flag = false;
outer:
for (int i = 0; i < tableLen; i++) {
for (int j = 0; j < cityNum; j++) {
if (route[j] == tabuTable[i][j]) {
flag = true;
break outer;
}
}
}
return flag;
}
//更新禁忌表
void updateTabuTable(int[] neighbor) {
int i, j, k;
//删除禁忌表第一个编码,后面编码往前移动
for (i = 0; i < tableLen - 1; i++) {
for (j = 0; j < cityNum; j++)
tabuTable[i][j] = tabuTable[i + 1][j];
//新的编码加入禁忌表
for (k = 0; k < cityNum; k++) {
tabuTable[tableLen - 1][k] = neighbor[k];
}
}
}
void solution() {
initGroup();
int bestEval = evaluation(route);
UtilMeth.reproduce(optimalRoute, route);
for (int gen = 0; gen < MAX_GEN; gen++) {
int neighEval = Integer.MAX_VALUE;
int[] neigh = new int[cityNum];
for (int umg = 0; umg < UMG_SCOP; umg++) {
int[] newRoute = UtilMeth.reproduce(route);
moveOperator(newRoute);//邻域移动产生新解
if (!aspirationCriterion(newRoute)) {
int newEval = evaluation(newRoute);
if (newEval < neighEval) { //新解优于当前解
UtilMeth.reproduce(neigh, newRoute);
neighEval = newEval;
}
}
}
if (neighEval < bestEval) {
UtilMeth.reproduce(route, neigh);
bestEval = neighEval;
}
updateTabuTable(neigh);
}
print("最终路径:" + Arrays.toString(route));
print("最终距离:" + bestEval);
}
}
package tsp.tabusearch;
import static net.mindview.util.Print.print;
public class Test {
public static void main(String[] args) {
TabuSearch ts = new TabuSearch(20);
long t1 = System.currentTimeMillis();
ts.solution();
long t2 = System.currentTimeMillis();
print("求解耗时:"+(t2 - t1)/1000 +"s");
}
}
3次运行结果如下:
初始路径:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
初始距离:1854
最终路径:[2, 1, 10, 13, 15, 5, 9, 7, 3, 6, 0, 16, 19, 12, 8, 4, 14, 17, 18, 11]
最终距离:645
求解耗时:13s
初始路径:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
初始距离:1854
最终路径:[10, 1, 0, 6, 5, 9, 7, 3, 2, 4, 8, 11, 12, 13, 16, 15, 14, 17, 18, 19]
最终距离:886
求解耗时:13s
初始路径:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
初始距离:1854
最终路径:[6, 0, 1, 3, 7, 5, 2, 4, 8, 9, 10, 13, 12, 11, 18, 17, 14, 15, 19, 16]
最终距离:762
求解耗时:13s