数学建模常用算法:迭代局部搜索算法求解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


目录
相关文章
|
23小时前
|
缓存 算法 安全
Java中的数据结构与算法优化策略
Java中的数据结构与算法优化策略
|
1天前
|
XML 测试技术 数据格式
《手把手教你》系列基础篇(八十三)-java+ selenium自动化测试-框架设计基础-TestNG测试报告-下篇(详解教程)
【7月更文挑战第1天】使用TestNG自定义报告的简要说明: - TestNG提供默认的HTML和XML报告,但可通过实现IReporter接口创建自定义报告。 - 自定义报告器类需扩展`CustomReporter.java`,实现`generateReport()`方法,接收XML套房、测试结果及输出目录作为参数。
10 0
|
1天前
|
算法 安全 Java
Java中MD5加密算法的原理与实现详解
Java中MD5加密算法的原理与实现详解
|
1天前
|
缓存 算法 Java
如何优化Java中的递归算法?
如何优化Java中的递归算法?
|
1天前
|
算法 安全 Java
Java中MD5加密算法的原理与实现详解
Java中MD5加密算法的原理与实现详解
|
1天前
|
负载均衡 Java 测试技术
性能测试与负载均衡:保证Java应用的稳定性
性能测试与负载均衡:保证Java应用的稳定性
|
1天前
|
存储 算法 搜索推荐
Java数据结构与算法优化
Java数据结构与算法优化
|
2天前
|
算法 Java 数据处理
Java中MD5加密算法的实现
Java中MD5加密算法的实现
|
1天前
|
算法 索引
基于Prony算法的系统参数辨识matlab仿真
Prony算法在MATLAB2022a中用于信号分析,识别复指数信号成分。核心程序通过模拟信号X1,添加不同SNR的噪声,应用Prony方法处理并计算误差。算法基于离散序列的复指数叠加模型,通过构建矩阵并解线性方程组估计参数,实现LTI系统动态特性的辨识。
|
2天前
|
算法 安全 数据库
基于结点电压法的配电网状态估计算法matlab仿真
**摘要** 该程序实现了基于结点电压法的配电网状态估计算法,旨在提升数据的准确性和可靠性。在MATLAB2022a中运行,显示了状态估计过程中的电压和相位估计值,以及误差随迭代变化的图表。算法通过迭代计算雅可比矩阵,结合基尔霍夫定律解决线性方程组,估算网络节点电压。状态估计过程中应用了高斯-牛顿或莱文贝格-马夸尔特法,处理量测数据并考虑约束条件,以提高估计精度。程序结果以图形形式展示电压幅值和角度估计的比较,以及估计误差的演变,体现了算法在处理配电网状态估计问题的有效性。