java画禁忌搜索算法的效果图_基于禁忌搜索算法求解TSP问题(JAVA) | 学步园

本文介绍了旅行商问题(TSP)及其分类,并详细阐述了禁忌搜索算法的基本思想和实施步骤。通过JAVA代码展示了如何应用禁忌搜索算法解决对称TSP问题,以att48数据集为例,得出最优解。最后总结了禁忌算法在求解NP问题中的特点和适用范围。
摘要由CSDN通过智能技术生成

一、TSP问题

TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。TSP问题可以分为两类,一类是对称TSP问题(Symmetric TSP),另一类是非对称问题(Asymmetric TSP)。所有的TSP问题都可以用一个图(Graph)来描述:

V={c1, c2, …, ci, …, cn},i = 1,2, …, n,是所有城市的集合.ci表示第i个城市,

n为城市的数目;

E={(r, s): r,s∈ V}是所有城市之间连接的集合;

C = {crs: r,s∈ V}是所有城市之间连接的成本度量(一般为城市之间的距离);

如果crs = csr, 那么该TSP问题为对称的,否则为非对称的。

一个TSP问题可以表达为:

求解遍历图G = (V, E, C),所有的节点一次并且回到起始节点,使得连接这些节点的路径成本最低。

二、禁忌搜索

禁忌搜索(Tabu Search或Taboo Search,简称TS)的思想最早由Glover(1986)提出,它是对局部领域搜索的一种扩展,是一种全局逐步寻优算法,是对人类智力过程的一种模拟。其特点是采用禁忌技术,即用一个禁忌表记录下已经到达过的局部最优点,在下一次搜索中,利用禁忌表中的信息不再或有选择地搜索这些点,以此来跳出局部最优点。该算法可以克服爬山算法全局搜索能力不强的弱点。

在禁忌搜索算法中,首先按照随机方法产生一个初始解作为当前解,然后在当前解的邻域中搜索若干个解,取其中的最好解作为新的当前解。为了避免陷入局部最优解,这种优化方法允许一定的下山操作(使解的质量变差)。另外,为了避免对已搜索过的局部最优解的重复,禁忌搜索算法使用禁忌表记录已搜索的局部最优解的历史信息,这可在一定程度上使搜索过程避开局部极值点,从而开辟新的搜索区域。

禁忌搜索最重要的思想是标记对应已搜索的局部最优解的一些对象,并在进一步的迭代搜索中尽量避开这些对象(而不是绝对禁止循环),从而保证对不同的有效搜索途径的探索。禁忌搜索涉及到临域(neighborhood)、禁忌表(tabu list)、禁忌长度(tabu length)、候选解(candidate)、藐视准则(aspiration criterion)等概念。

禁忌搜索算法实施步骤:

1366209078_2795.png

三、禁忌搜索算法求解TSP问题

在该JAVA实现中我们选择使用tsplib上的数据att48,这是一个对称TSP问题,城市规模为48,其最优值为10628.其距离计算方法下图所示:

1366110497_2052.png

具体代码如下:

package noah;

import java.io.BufferedReader;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.util.Random;

public class Tabu {

private int MAX_GEN;// 迭代次数

private int N;// 每次搜索邻居个数

private int ll;// 禁忌长度

private int cityNum; // 城市数量,编码长度

private int[][] distance; // 距离矩阵

private int bestT;// 最佳出现代数

private int[] Ghh;// 初始路径编码

private int[] bestGh;// 最好的路径编码

private int bestEvaluation;

private int[] LocalGhh;// 当代最好编码

private int localEvaluation;

private int[] tempGhh;// 存放临时编码

private int tempEvaluation;

private int[][] jinji;// 禁忌表

private int t;// 当前代数

private Random random;

public Tabu() {

}

/**

* constructor of GA

*

* @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;

}

// 给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默

@SuppressWarnings("resource")

/**

* 初始化Tabu算法类

* @param filename 数据文件名,该文件存储所有城市节点坐标数据

* @throws IOException

*/

private void init(String filename) throws IOException {

// 读取数据

int[] x;

int[] y;

String strbuff;

BufferedReader data = new BufferedReader(new InputStreamReader(

new FileInputStream(filename)));

distance = new int[cityNum][cityNum];

x = new int[cityNum];

y = new int[cityNum];

for (int i = 0; i < cityNum; i++) {

// 读取一行数据,数据格式1 6734 1453

strbuff = data.readLine();

// 字符分割

String[] strcol = strbuff.split(" ");

x[i] = Integer.valueOf(strcol[1]);// x坐标

y[i] = Integer.valueOf(strcol[2]);// y坐标

}

// 计算距离矩阵

// ,针对具体问题,距离计算方法也不一样,此处用的是att48作为案例,它有48个城市,距离计算方法为伪欧氏距离,最优值为10628

for (int i = 0; i < cityNum - 1; i++) {

distance[i][i] = 0; // 对角线为0

for (int j = i + 1; j < cityNum; j++) {

double rij = Math

.sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])

* (y[i] - y[j])) / 10.0);

// 四舍五入,取整

int tij = (int) Math.round(rij);

if (tij < rij) {

distance[i][j] = tij + 1;

distance[j][i] = distance[i][j];

} else {

distance[i][j] = tij;

distance[j][i] = distance[i][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());

/*

* for(int i=0;i

* System.out.print(distance[i][j]+","); } System.out.println(); }

*/

}

// 初始化编码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 int evaluate(int[] chr) {

// 0123

int 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) {

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.println("最佳长度出现代数:");

System.out.println(bestT);

System.out.println("最佳长度");

System.out.println(bestEvaluation);

System.out.println("最佳路径:");

for (int i = 0; i < cityNum; i++) {

System.out.print(bestGh[i] + ",");

}

}

/**

* @param args

* @throws IOException

*/

public static void main(String[] args) throws IOException {

System.out.println("Start....");

Tabu tabu = new Tabu(48, 1000, 200, 20);

tabu.init("c://data.txt");

tabu.solve();

}

}

运行结果截图:

1366209264_5288.png

四、总结

禁忌算法其主要特点是在搜索开始阶段,解的质量提高很快,随着搜索过程的继续,解的质量的提高速度逐渐放缓,甚至在很长的搜索阶段内解的质量没有太大提高,适合中小规模的NP问题求解,整体效率比较均衡。

(接上一篇爬山算法,个人计划TSP问题系列大概会有四五种算法来求解,敬请各位期待!)

注:本文部分内容来源于网络,但程序以及分析结果属于本人成果,转载请注明!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值