基于禁忌搜索的TSP问题建模求解(Java)

一、禁忌搜索算法步骤与流程

  1. 邻域移动:邻域移动是从一个解产生另一个解的途径。本文采用2-opt。
  2. 禁忌表:通常记录前若干次的移动,禁止这些移动在近期内返回。在迭代固定次数后,禁忌表释放这些移动,重新参加运算。
  3. 选择策略:即择优规则,是对当前的邻域移动选择而采用的准则。当前采用最广泛的两类策略是最好解优先策略和第一个改进解优先策略。本文采用最好解优先
  4. 特赦准则:考虑邻域内所有解(包括禁忌解),并逐一进行评估,发现出色的禁忌解,将其解禁。
  5. 终止规则:迭代次数控制。

算法流程图如下所示:

二、基于禁忌搜索的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 

三、参考资料

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值