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

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

代码

package com.dam.heuristic.ils.test;
import java.util.*;
import static com.dam.heuristic.ils.ShuJuMoShuShi.City.CITY_SIZE;
/**
 * 迭代局部搜索
 */
public class IlsApi {
    //最大的迭代次数
    private int maxGen;
    //可接受最大没有提高解的次数
    private int acceptMaxNotImproveSolutionNum;
    //城市数量
    private int cityNum;
    //扰动最优序列时,可接受的新序列对应目标函数值和最优目标函数值之间的差值
    private double acceptDifferentLimit;
    //扰动最优序列时的代数
    private int genNum;
    //距离矩阵
    private double[][] distanceMatrix;
    public IlsApi(int maxGen, int acceptMaxNotImproveSolutionNum, double acceptDifferentLimit, int genNum, double[][] distanceMatrix) {
        this.maxGen = maxGen;
        this.acceptMaxNotImproveSolutionNum = acceptMaxNotImproveSolutionNum;
        this.distanceMatrix = distanceMatrix;
        this.acceptDifferentLimit = acceptDifferentLimit;
        this.genNum = genNum;
        this.cityNum = distanceMatrix[0].length;
    }
    public int[] solve() {
        定义变量
        long startTime = System.currentTimeMillis();
        ///最优解
        //最优代数
        int bestT = 0;
        //序列
        int[] localSequence = new int[this.distanceMatrix[0].length];
        //最优序列
        int[] bestSequence;
        //存储求得最优解的时间
        long bestTime = 0;
        //最优序列对应的目标函数值
        double bestObjectValue = 0;
        生成初始序列
        this.generateInitialSequence(localSequence);
        //初始化bestSequence,刚开始的最优序列为初始序列
        bestSequence = localSequence.clone();
        bestObjectValue = this.getObjectValue(bestSequence);
        初始搜索
        bestSequence = this.localSearch(bestSequence, bestObjectValue);
        bestObjectValue = this.getObjectValue(bestSequence);
        迭代优化
        for (int t = 0; t < this.maxGen; t++) {
            ///多次扰动最优序列,当得到的序列没那么差,或者到达this.genNum限制之后,停止扰动
            int[] tempSequence = new int[this.cityNum];
            double tempObjectValue = Double.MAX_VALUE;
            for (int i = 0; i < this.genNum; i++) {
                //扰动最优解产生新解
                tempSequence = this.perturbation(bestSequence);
                tempObjectValue = this.getObjectValue(tempSequence);
                if (tempObjectValue - bestObjectValue < this.acceptDifferentLimit) {
                    break;
                }
            }
            //对tempSequence进行局部搜索
            tempSequence = this.localSearch(tempSequence, tempObjectValue);
            tempObjectValue = this.getObjectValue(tempSequence);
            //更新最优解
            if (tempObjectValue < bestObjectValue) {
                bestObjectValue = tempObjectValue;
                bestSequence = tempSequence.clone();
                bestT = t;
                bestTime = System.currentTimeMillis() - startTime;
//                System.out.println("当前最优目标函数值:" + tempObjectValue);
//                System.out.println("计算时间为:" + bestTime + "ms");
            }
        }
        System.out.println("最佳迭代次数:" + bestT);
        System.out.println("最优目标函数值:" + bestObjectValue);
        System.out.println("最优解对应序列:" + Arrays.toString(bestSequence));
        System.out.println("计算时间为:" + (System.currentTimeMillis() - startTime) + "ms");
        System.out.println(this.getObjectValue(bestSequence));
        return bestSequence;
    }
    /**
     * 局部搜索更优解
     *
     * @param bestSequence
     * @param bestObjectValue
     * @return 当前所搜索到的最优解对应的目标函数值
     */
    public int[] localSearch(int[] bestSequence, double bestObjectValue) {
        int count = 0;
        while (count++ <= this.acceptMaxNotImproveSolutionNum) {
            for (int i = 0; i < this.cityNum - 1; i++) {
                for (int j = i + 1; j < this.cityNum; j++) {
                    //通过反转indexI和indexJ之间的元素,产生新的序列
                    int[] tempSequence = this.generateNewSequenceBySwapTwoElementWithTwoAppointIndex(bestSequence, i, j);
                    double tempObjectValue = this.getObjectValue(tempSequence);
                    //更新最优解
                    if (tempObjectValue < bestObjectValue) {
                        count = 0;
                        bestObjectValue = tempObjectValue;
                        bestSequence = tempSequence.clone();
                        break;
                    }
                }
            }
        }
        return bestSequence;
    }
    /**
     * 通过将bestSequence的元素分为四组,然后改变四个组的组序,获得新序列
     *
     * @param bestSequence
     */
    public int[] perturbation(int[] bestSequence) {
        Random random = new Random();
        int[] newSequence = new int[bestSequence.length];
        获取五个index,这五个index将bestSequence划分为四组(并非均分)
        //this.cityNum - 2的原因是:indexArr[0]和indexArr[4]都已经确定
        int elementNumInOneGroup = (this.cityNum - 2) / 3;
        int[] indexArr = new int[5];
        indexArr[0] = 0;
        indexArr[1] = random.nextInt(elementNumInOneGroup) + 1;
        indexArr[2] = random.nextInt(elementNumInOneGroup) + elementNumInOneGroup + 1;
        indexArr[3] = random.nextInt(elementNumInOneGroup) + elementNumInOneGroup * 2 + 1;
        indexArr[4] = this.cityNum;
        将组别[0,1,2,3]对应的元素赋值给newSequence
        ///将组序打乱
        List<Integer> groupCodeList = new ArrayList<>();
        //将[0,1,2,3]赋值给集合
        Collections.addAll(groupCodeList, 0, 1, 2, 3);
        //随机打乱
        Collections.shuffle(groupCodeList);
        ///赋值
        int index = 0;
        for (int i = 0; i < groupCodeList.size(); i++) {
            for (int j = indexArr[groupCodeList.get(i)]; j < indexArr[groupCodeList.get(i) + 1]; j++) {
                newSequence[index] = bestSequence[j];
                index++;
            }
        }
        return newSequence;
    }
    /**
     * 通过反转indexI和indexJ之间的元素,产生新的序列
     *
     * @param sequence
     * @param indexI
     * @param indexJ
     */
    public int[] generateNewSequenceBySwapTwoElementWithTwoAppointIndex(int[] sequence, int indexI, int indexJ) {
        //克隆出新序列
        int[] newSequence = sequence.clone();
        int temp;
        //不但交换indexI和indexJ对应的元素
        while (indexI < indexJ) {
            temp = newSequence[indexI];
            newSequence[indexI] = newSequence[indexJ];
            newSequence[indexJ] = temp;
            indexI++;
            indexJ--;
        }
        return newSequence;
    }
    /**
     * 生成初始序列
     */
    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;
    }
}

测试

package com.dam.heuristic.ils.test;
import com.alibaba.fastjson.JSON;
import com.dam.heuristic.ts.test.TsApi;
import com.dam.heuristic.util.paint.PaintTspResult;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
public class IlsMainRun {
    public static void main(String[] args) throws Exception {
        声明变量
        //距离矩阵,可以直接获取任意两个编号城市的距离
        double[][] distanceMatrix;
        //存储每个城市对应的x,y坐标
        double[][] cityPositionArr;
        读取数据
        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];
        cityPositionArr = new double[cityDataArr.length][2];
        for (int i = 0; i < cityDataArr.length; i++) {
            String[] city1Arr = cityDataArr[i].split(" ");
            cityPositionArr[i][0] = Double.valueOf(city1Arr[1]);
            cityPositionArr[i][1] = Double.valueOf(city1Arr[2]);
            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));
        }*/
        IlsApi ilsApi = new IlsApi(10, 50, 500, 5, distanceMatrix);
        int[] bestSequence = ilsApi.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) {
            }
        }
    }
}



最佳迭代次数:36
最优目标函数值:10698.532235769424
最优解对应序列:[35, 29, 42, 16, 26, 18, 36, 5, 27, 6, 17, 43, 30, 37, 8, 7, 0, 15, 21, 2, 33, 40, 28, 1, 25, 3, 34, 44, 9, 23, 41, 4, 47, 38, 31, 20, 12, 24, 13, 22, 10, 46, 19, 11, 39, 14, 32, 45]
计算时间为:1121ms


目录
相关文章
|
3月前
|
机器学习/深度学习 算法 安全
【无人机三维路径规划】基于非支配排序的鲸鱼优化算法NSWOA与多目标螳螂搜索算法MOMSA求解无人机三维路径规划研究(Matlab代码实现)
【无人机三维路径规划】基于非支配排序的鲸鱼优化算法NSWOA与多目标螳螂搜索算法MOMSA求解无人机三维路径规划研究(Matlab代码实现)
247 5
|
3月前
|
机器学习/深度学习 算法 安全
【无人机三维路径规划】多目标螳螂搜索算法MOMSA与非支配排序的鲸鱼优化算法NSWOA求解无人机三维路径规划研究(Matlab代码实现)
【无人机三维路径规划】多目标螳螂搜索算法MOMSA与非支配排序的鲸鱼优化算法NSWOA求解无人机三维路径规划研究(Matlab代码实现)
187 0
|
2月前
|
算法 数据可视化 测试技术
HNSW算法实战:用分层图索引替换k-NN暴力搜索
HNSW是一种高效向量检索算法,通过分层图结构实现近似最近邻的对数时间搜索,显著降低查询延迟。相比暴力搜索,它在保持高召回率的同时,将性能提升数十倍,广泛应用于大规模RAG系统。
227 10
HNSW算法实战:用分层图索引替换k-NN暴力搜索
|
7月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
2月前
|
机器学习/深度学习 算法 机器人
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
183 8
|
8月前
|
存储 算法 调度
基于和声搜索优化算法的机器工作调度matlab仿真,输出甘特图
本程序基于和声搜索优化算法(Harmony Search, HS),实现机器工作调度的MATLAB仿真,输出甘特图展示调度结果。算法通过模拟音乐家即兴演奏寻找最佳和声的过程,优化任务在不同机器上的执行顺序,以最小化完成时间和最大化资源利用率为目标。程序适用于MATLAB 2022A版本,运行后无水印。核心参数包括和声记忆大小(HMS)等,适应度函数用于建模优化目标。附带完整代码与运行结果展示。
247 24
|
4月前
|
机器学习/深度学习 算法 文件存储
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
神经架构搜索(NAS)正被广泛应用于大模型及语言/视觉模型设计,如LangVision-LoRA-NAS、Jet-Nemotron等。本文回顾NAS核心技术,解析其自动化设计原理,探讨强化学习、进化算法与梯度方法的应用与差异,揭示NAS在大模型时代的潜力与挑战。
1150 6
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
|
7月前
|
人工智能 算法 NoSQL
LRU算法的Java实现
LRU(Least Recently Used)算法用于淘汰最近最少使用的数据,常应用于内存管理策略中。在Redis中,通过`maxmemory-policy`配置实现不同淘汰策略,如`allkeys-lru`和`volatile-lru`等,采用采样方式近似LRU以优化性能。Java中可通过`LinkedHashMap`轻松实现LRUCache,利用其`accessOrder`特性和`removeEldestEntry`方法完成缓存淘汰逻辑,代码简洁高效。
316 0
|
2月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
8月前
|
人工智能 自然语言处理 算法
阿里云 AI 搜索开放平台:从算法到业务——AI 搜索驱动企业智能化升级
本文介绍了阿里云 AI 搜索开放平台的技术的特点及其在各行业的应用。
893 3

热门文章

最新文章