模拟退火算法解决TSP(旅行商)问题

一、TSP问题描述

旅行商问题是由英国数学家Thomas Penyngton Kirkman 和爱尔兰数学家 Sir William Rowan Hamilton 在 19 世纪提出的数学问题,是图论中的一个经典问题,也是一个典型的组合优化问题。问题是这样描述的:一名旅行商人要到若干个城市去销售商品,已知城市的个数 n 和任意两个城市间的距离,需要找到一条从任意城市 A 出发,途经所有城市一次并且仅一次,最后返回到城市 A 的路线,使得总的距离最小。、

二、模拟退火算法基本原理

模拟退火(SA)算法的基本原理如下: 
(1)给定初始温度T0和初始点x0,计算该点的函数值f(x0);
(2)随机产生扰动△x,得到新点x′=x+△x,计算新点的函数值f(x′)和差△f=f(x′)-f(x);
(3)若△f≤0,则接受新点,作为下一次模拟退火的初始点;
(4)若△f>0,则计算新点的接受概率:p(△f)=xp(-△f/(K T)),产生[0,1]区间上均匀分布的伪随机数r,r∈ [0,1]。若p≤r,则接受新点作为下一次模拟的初始点;否则仍取原来的点作为下一次模拟退火的初始点。以上步骤称为Metropolis过程。逐渐降低控制温度,重复Metropolis过程,直至达到结束准则,就构成了SA算法。

三、利用模拟退火算法实现TSP问题

1,定义城市实体类,实现计算城市间的距离
public class City {
    // 地球赤道半径
    private static final double ERATH_EQUATORIAL_RADIUS = 6378.1370D;
    private static final double CONCVERT_DEGREES_TO_RADIANS = Math.PI / 180;
    // 经度
    private double longitude;
    // 纬度
    private double latitude;
    // 城市名
    private String name;

    public City(double longitude,double latitude,String name){
        super();
        this.longitude=longitude;
        this.latitude=latitude;
        this.name=name;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return this.name;
    }

    /**
     * 计算传入城市与当前城市的实际距离
     * @param city
     * @return
     */
    public double measureDistance(City city){
        double deltaLongitude = deg2rad(city.getLongitude() - this.getLongitude());
        double deltaLatitude = deg2rad(city.getLatitude() - this.getLatitude());
        double a = Math.pow(Math.sin(deltaLatitude / 2D), 2D)
                + Math.cos(deg2rad(this.getLatitude()))
                * Math.cos(deg2rad(city.getLatitude()))
                * Math.pow(Math.sin(deltaLongitude / 2D), 2D);
        return  ERATH_EQUATORIAL_RADIUS * 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
    }

    // 转化为弧度
    private double deg2rad(double deg) {
        return deg * CONCVERT_DEGREES_TO_RADIANS;
    }
}
2 定义路径实体类
public class Route {
    private ArrayList cities=new ArrayList<>();

    /**
     * 构造方法
     * 随机打乱cities排序
     * @param cities
     */
    public Route(ArrayList cities){
        this.cities.addAll(cities);
        Collections.shuffle(this.cities);
    }

    public Route(Route route){
        for (City city:route.getCities()){
            this.cities.add(city);
        }
    }
    /**
     * 计算城市间总距离
     * @return
     */
    public double getTotalDistance() {
        int citiesSize = this.cities.size();
        double sum = 0D;
        int index = 0;
        while(index < citiesSize - 1) {
            sum += this.cities.get(index).measureDistance(this.cities.get(index + 1));
            index++;
        }
        sum += this.cities.get(citiesSize - 1).measureDistance(this.cities.get(0));
        return sum;
    }

    public String getTotalStringDistance() {
        String returnString = String.format("%.2f", this.getTotalDistance());
        return returnString;
    }

    public ArrayList getCities() {
        return cities;
    }

    public void setCities(ArrayList cities) {
        this.cities = cities;
    }

    @Override
    public String toString() {
        return Arrays.toString(cities.toArray());
    }
}
3,定义模拟退火算法
public class SA {
    // 最大迭代次数
    public static final int ITERATIONS_BEFORE_MAXIMUM = 1500;
    public double T=1500;
    public static final double T_min=1e-5;
    public static final double delta=0.98;

    /**
     * 模拟退火算法解决TSP问题
     * **/
    public Route findShortRouteSA(Route currentRoute){
        Route adjacentRoute;
        int iterToMaximumCounter=0;
        String compareRoutes=null;
        while (T>T_min){
            adjacentRoute=obtainAdjacentRoute(new Route(currentRoute));
            if (adjacentRoute.getTotalDistance()<=currentRoute.getTotalDistance()){
                compareRoutes = "<= (更新)";
                iterToMaximumCounter = 0;
                currentRoute = new Route(adjacentRoute);
            }else{
                double p=Math.random();
                if (Math.exp(adjacentRoute.getTotalDistance()-currentRoute.getTotalDistance()/T)>p){
                    compareRoutes = "<= (更新)";
                    iterToMaximumCounter = 0;
                    currentRoute = new Route(adjacentRoute);
                }else {
                    compareRoutes = "> (保持) - 迭代次数 # " + iterToMaximumCounter;
                    iterToMaximumCounter++;
                }
            }
            System.out.println("       | " + compareRoutes);
            System.out.print(currentRoute + " |      " + currentRoute.getTotalStringDistance());
            T=delta*T;
        }
        System.out.println("       | 可能的最优解");
        return  currentRoute;
    }

    /**
     * 随机交换两个城市位置
     * @param route
     * @return
     */
    public Route obtainAdjacentRoute(Route route) {
        int x1 = 0, x2 = 0;
        while(x1 == x2) {
            x1 = (int) (route.getCities().size() * Math.random());
            x2 = (int) (route.getCities().size() * Math.random());
        }

        City city1 = route.getCities().get(x1);
        City city2 = route.getCities().get(x2);

        // swap two stochastic cities
        route.getCities().set(x2, city1);
        route.getCities().set(x1, city2);
        return route;
    }

}
4,定义test类测试
public class Driver {
    private ArrayList initialCities = new ArrayList(Arrays.asList(
            new City(116.41667, 39.91667, "北京"),
            new City(121.43333, 34.50000, "上海"),
            new City(113.00000, 28.21667, "长沙"),
            new City(106.26667, 38.46667, "银川"),
            new City(109.50000, 18.20000, "三亚"),
            new City(112.53333, 37.86667, "太原"),
            new City(91.00000, 29.60000, "拉萨"),
            new City(102.73333,  25.05000, "昆明"),
            new City(126.63333, 45.75000, "哈尔滨"),
            new City(113.65000, 34.76667, "郑州"),
            new City(113.50000, 22.20000, "澳门")));

    public static void main(String[] args) {
        Driver driver=new Driver();
        Route route=new Route(driver.initialCities);
        new SA().findShortRouteSA(route);
    }

}
5,代码运行结果截图

[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 389
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 390
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 391
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 392
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 393
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 394
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 395
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 396
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 397
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 398
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 399
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 400
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 401
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 402
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 403
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 404
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 405
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | > (保持) - 迭代次数 # 406
[北京, 哈尔滨, 上海, 郑州, 长沙, 澳门, 三亚, 昆明, 拉萨, 银川, 太原] |      10085.38       | 可能的最优解

Process finished with exit code 0


你可能感兴趣的:(算法,算法,数学,优化,软件,JAVA)