数学建模常用算法:禁忌搜索算法求解tsp问题+att48算例测试【java实现--详细注释】

简介: 数学建模常用算法:禁忌搜索算法求解tsp问题+att48算例测试【java实现--详细注释】

代码

package com.dam.heuristic.ts.test;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
/**
 * 禁忌搜索
 */
public class TsApi {
    //当前已占用的禁忌表长度
    private int curUsedTabooLen;
    //禁忌表长度
    private int tabooTableLength;
    //序列长度
    private int sequenceLen;
    //最大的迭代次数
    private int maxGen;
    //每次搜索领域的个数
    private int neighbourNum;
    //距离矩阵
    private double[][] distanceMatrix;
    /**
     * 构造函数,用于创建对象
     *
     * @param tabooTableLength
     * @param maxGen
     * @param neighbourNum
     * @param distanceMatrix
     */
    public TsApi(int tabooTableLength, int maxGen, int neighbourNum, double[][] distanceMatrix) {
        this.curUsedTabooLen = 0;
        this.tabooTableLength = tabooTableLength;
        this.sequenceLen = distanceMatrix[0].length;
        this.maxGen = maxGen;
        this.neighbourNum = neighbourNum;
        this.distanceMatrix = distanceMatrix;
    }
    /**
     * 禁忌搜素算法接口
     */
    public void solve() {
        定义变量
        long startTime = System.currentTimeMillis();
        //禁忌表
        int[][] tabooTable = new int[this.tabooTableLength][this.sequenceLen];//禁忌表
        //代数
        int t = 0;
        ///最优解
        //最优代数
        int bestT = 0;
        //序列
        int[] localSequence = new int[this.sequenceLen];
        //最优序列
        int[] bestSequence;
        //存储暂时序列
        int[] tempSequenceInNeighbour;
        //存储邻居中最好的序列对
        int[] bestSequenceInNeighbour = null;
        //存储求得最优解的时间
        long bestTime = 0;
        //最优序列对应的目标函数值
        double bestObjectValue = 0;
        生成初始序列
        this.generateInitialSequence(localSequence);
        //初始化bestSequence,刚开始的最优序列为初始序列
        bestSequence = localSequence.clone();
        bestObjectValue = this.getObjectValue(bestSequence);
        System.out.println("初始序列:" + Arrays.toString(bestSequence));
        System.out.println("初始目标函数值:" + bestObjectValue);
        对序列进行迭代优化
        while (t++ < this.maxGen) {
            //如果目标是最小化函数值,则初始化为Double.MAX_VALUE;反之,初始化为-Double.MAX_VALUE
            double bestObjectValueInNeighbour = Double.MAX_VALUE;
            //neighbourNum :搜索领域个数
            int i = 0;
            while (i < this.neighbourNum) {
                //产生一条新序列
                tempSequenceInNeighbour = this.generateNewSequence(localSequence);
                //判断是否在禁忌表中
                if (isSequenceAtTabooTable(tabooTable, tempSequenceInNeighbour) == false) {
                    //搜索到的邻居序列不在禁忌表中,i才加一
                    i++;
                    //不在禁忌表中时,执行下面的计算,获取tempSequence所对应的目标函数值
                    double tempObjectValue = this.getObjectValue(tempSequenceInNeighbour);
                    //更新最优解
                    if (tempObjectValue < bestObjectValueInNeighbour) {
                        bestObjectValueInNeighbour = tempObjectValue;
                        bestSequenceInNeighbour = tempSequenceInNeighbour.clone();
                    }
                }
            }
            //解禁忌表,LocalGh加入禁忌表
            this.addSequenceToTabooTable(tabooTable, bestSequenceInNeighbour);
            //接受邻居序列中的最优序列,让算法可以跳出局部最优
            localSequence = bestSequenceInNeighbour.clone();
//            System.out.println("当前最优邻居目标函数值:" + bestObjectValueInNeighbour);
//            System.out.println("当前最优邻居解:" + Arrays.toString(bestSequenceInNeighbour));
//            System.out.println("当前最优解为:" + bestObjectValue + ",求得最优解的时间:" + bestTime + "ms");
            //更新全局最优解
            if (bestObjectValueInNeighbour < bestObjectValue) {
                //更新最优代数
                bestT = t;
                //更新最优目标函数值
                bestObjectValue = bestObjectValueInNeighbour;
                //更新最优序列
                bestSequence = localSequence.clone();
                System.out.println("----------------------------------------------------------------------------------------------------------------");
                System.out.println("当前代数:" + t);
                System.out.println("当前最优解为:" + bestObjectValue);
                bestTime = (System.currentTimeMillis() - startTime);
                System.out.println("当前计算时间为:" + bestTime + "ms");
                System.out.println("----------------------------------------------------------------------------------------------------------------");
            }
//            System.out.println("当前计算时间为:" + (System.currentTimeMillis() - startTime) + "ms");
//            System.out.println("----------------------------------------------------------------------------------------------------------------");
        }
        System.out.println("最佳迭代次数:" + bestT);
        System.out.println("最优目标函数值:" + bestObjectValue);
        System.out.println("最优解对应序列:" + Arrays.toString(bestSequence));
        System.out.println("计算时间为:" + (System.currentTimeMillis() - startTime) + "ms");
    }
    /**
     * 生成初始序列
     */
    public void generateInitialSequence(int[] sequence) {
      /*  HashSet<Integer> sequenceSet = new HashSet<>();
        for (int i = 1; i < sequence.length; i++) {
            sequenceSet.add(i);
        }
        //贪婪算法获取初始序列,从城市0开始旅行,即城市0为起点城市
        sequence[0] = 0;
        //每次获取离当前城市最近的城市,并加入到sequence
        for (int i = 1; i < sequence.length; i++) {
            //寻找离i-1城市最近的城市,即确定第i个城市是哪个
            double smallDistance = Double.MAX_VALUE;
            int curCity = 0;
            for (Integer j : sequenceSet) {
                if (this.distanceMatrix[sequence[i - 1]][j] < smallDistance && j != sequence[i - 1] ) {
                    smallDistance = this.distanceMatrix[sequence[i - 1]][j];
                    curCity = j;
                }
            }
            sequence[i] = curCity;
            sequenceSet.remove(curCity);
        }*/
        for (int i = 0; i < sequence.length; i++) {
            sequence[i] = i;
        }
    }
    /**
     * 根据当前序列获取目标函数值
     *
     * @param sequence
     * @return
     */
    public double getObjectValue(int[] sequence) {
        double objectValue = 0;
        //计算从第0个城市到最后一个城市的路程
        for (int i = 0; i < sequence.length - 1; i++) {
            objectValue += this.distanceMatrix[sequence[i]][sequence[i + 1]];
        }
        //计算最后一个城市到第0个城市的路程
        objectValue += this.distanceMatrix[sequence[0]][sequence[sequence.length - 1]];
        return objectValue;
    }
    /**
     * 生产新序列
     *
     * @param sequence
     * @return
     */
    public int[] generateNewSequence(int[] sequence) {
        int[] sequenceClone = sequence.clone();
        //对序列中的元素进行打乱,即可产生新的序列
        Random random = new Random();
        int i = random.nextInt(sequence.length);
        int j = random.nextInt(sequence.length);
        while (i == j) {
            j = random.nextInt(sequence.length);
        }
        int temp = sequenceClone[i];
        sequenceClone[i] = sequenceClone[j];
        sequenceClone[j] = temp;
        return sequenceClone;
    }
    /**
     * 将序列加入禁忌表,如果禁忌表已满,根据先进先出的原则移除最早进入禁忌表的序列
     *
     * @param tabooTable
     * @param sequence
     */
    public void addSequenceToTabooTable(int[][] tabooTable, int[] sequence) {
        if (this.curUsedTabooLen < this.tabooTableLength) {
            //如果当前禁忌表还有空位,则直接加入即可
            for (int i = 0; i < sequence.length; i++) {
                tabooTable[this.curUsedTabooLen][i] = sequence[i];
            }
            this.curUsedTabooLen++;
        } else {
            //如果禁忌表已经满了,则移除第一个进表的路径,添加新的路径到禁忌表末尾
            //后面的禁忌编码全部向前移动一位,覆盖掉当前第一个禁忌编码
            for (int i = 0; i < tabooTable.length - 1; i++) {
                tabooTable[i] = tabooTable[i + 1].clone();
            }
            //将sequence加入到禁忌队列的最后
            for (int i = 0; i < sequence.length; i++) {
                tabooTable[tabooTable.length - 1][i] = sequence[i];
            }
        }
    }
    /**
     * 判断序列对是否存在于禁忌表中
     *
     * @param tabooTable
     * @param sequence
     * @return
     */
    public boolean isSequenceAtTabooTable(int[][] tabooTable, int[] sequence) {
        for (int i = 0; i < tabooTable.length; i++) {
            boolean flag = true;
            for (int j = 0; j < sequence.length; j++) {
                if (sequence[j] != tabooTable[i][j]) {
                    flag = false;
                    break;
                }
            }
            if (flag == true) {
                return true;
            }
        }
        return false;
    }
}


测试

package com.dam.heuristic.ts.test;
import java.io.File;
import java.io.FileInputStream;
public class TsMainRun {
    public static void main(String[] args) throws Exception {
        声明变量
        //距离矩阵,可以直接获取任意两个编号城市的距离
        double[][] distanceMatrix;
        读取数据
        String data = read(new File("src/main/java/com/data/tsp/att48.txt"), "GBK");
        String[] cityDataArr = data.split("\n");
        //初始化数组
        distanceMatrix = new double[cityDataArr.length][cityDataArr.length];
        for (int i = 0; i < cityDataArr.length; i++) {
            String[] city1Arr = cityDataArr[i].split(" ");
            int cityOne = Integer.valueOf(city1Arr[0]);
            for (int j = 0; j < i; j++) {
                String[] city2Arr = cityDataArr[j].split(" ");
                int cityTwo = Integer.valueOf(city2Arr[0]);
                if (cityOne == cityTwo) {
                    distanceMatrix[cityOne - 1][cityTwo - 1] = 0;
                } else {
                    distanceMatrix[cityOne - 1][cityTwo - 1] = getDistance(Double.valueOf(city1Arr[1]), Double.valueOf(city1Arr[2]), Double.valueOf(city2Arr[1]), Double.valueOf(city2Arr[2]));
                    //对称赋值
                    distanceMatrix[cityTwo - 1][cityOne - 1] = distanceMatrix[cityOne - 1][cityTwo - 1];
                }
            }
        }
     /*   System.out.println("输出距离矩阵-------------------------------------------------------------------");
        for (double[] matrix : distanceMatrix) {
            System.out.println(Arrays.toString(matrix));
        }*/
        TsApi TSApi = new TsApi(20, 100000, 100, distanceMatrix);
        TSApi.solve();
    }
    /**
     * 给定两个城市坐标,获取两个城市的直线距离
     *
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @return
     */
    private static double getDistance(double x1, double y1, double x2, double y2) {
        return Math.sqrt((Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2))/10);
    }
    private static String read(File f, String charset) throws Exception {
        FileInputStream fstream = new FileInputStream(f);
        try {
            int fileSize = (int) f.length();
            if (fileSize > 1024 * 512) {
                throw new Exception("File too large to read! size=" + fileSize);
            }
            byte[] buffer = new byte[fileSize];
            fstream.read(buffer);
            return new String(buffer, charset);
        } finally {
            try {
                fstream.close();
            } catch (Exception e) {
            }
        }
    }
}


最佳迭代次数:37144
最优目标函数值:10666.643562660376
最优解对应序列:[34, 44, 9, 23, 41, 4, 47, 38, 31, 20, 12, 24, 13, 22, 10, 46, 19, 11, 14, 32, 45, 35, 29, 42, 16, 26, 18, 36, 5, 27, 6, 17, 43, 30, 37, 8, 7, 0, 39, 2, 21, 15, 40, 33, 28, 1, 3, 25]
计算时间为:2375ms
目录
相关文章
|
17天前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
50 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
2月前
|
Java
Java搜索与替换
Java搜索与替换
24 4
Java搜索与替换
|
22天前
|
XML Java Maven
在 Cucumber 测试中自动将 Cucumber 数据表映射到 Java 对象
在 Cucumber 测试中自动将 Cucumber 数据表映射到 Java 对象
41 7
|
18天前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
29 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
22天前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
50 2
|
27天前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
15 0
|
3月前
|
算法
测试工程师的技能升级:LeetCode算法挑战与职业成长
这篇文章通过作者亲身体验LeetCode算法题的过程,探讨了测试工程师学习算法的重要性,并强调了算法技能对于测试职业成长的必要性。
62 1
测试工程师的技能升级:LeetCode算法挑战与职业成长
|
3月前
|
XML Java 测试技术
Selenium WebDriver自动化测试(基础篇):不得不掌握的Java基础
关于Selenium WebDriver自动化测试的Java基础篇,涵盖了Java的变量、数据类型、字符串操作、运算符、流程控制、面向对象编程、关键字用法、权限修饰符、异常处理和IO流等基础知识点,为进行自动化测试提供了必要的Java语言基础。
87 1
|
3月前
|
算法 Java
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
49 6
|
3月前
|
搜索推荐 算法 Java
经典排序算法之-----选择排序(Java实现)
这篇文章通过Java代码示例详细解释了选择排序算法的实现过程,包括算法的基本思想、核心代码、辅助函数以及测试结果,展示了如何通过选择排序对数组进行升序排列。
经典排序算法之-----选择排序(Java实现)