zoukankan      html  css  js  c++  java
  • 模拟退火算法初探

    参考来源:

    https://am207.github.io/2017/wiki/lab4.html#traveling-salesman-problem-tsp

    《现代优化理论与方法》黄庆道等编.上册P165,

    在解决旅行商问题时,用到了模拟退火算法。

    1.Metropolis准则

    该准则表示以概率接受新状态。假设在温度$t$时刻,由当前状态$n$产生新状态$n+1$,两者能量分别为$E(n)$和$E(n+1)$.

    则 接受新状态的概率为

    (1) 如果$E(n+1) < E(n)$,则概率1接受新状态$E(n+1)$

    (2)如果$E(n+1) geq E(n)$,则判断条件[e^{-frac{E(n+1)-E(n)}{T}} > [0,1)区间内的随机数] 

    如果成立接受新状态$E(n+1)$,否则保留$E(n)$为当前状态。

    算法思路:

    (1)给定初温$t=t_0$,随机指定初始状态$s=s_0,k=0$。

    (2)一般迭代:

    重复以下过程:

    产生新状态 $s_j = Generate(s)$;

    if min${1, exp[-(C(s_j)-C(s))]} geq random[0,1]$ , 则$s=s_j$

    直到抽样稳定准则满足

    退温$t_k = update(t_k),令k=k+1$

    (3)输出算法结果。

    自己用python写了一个类似的,有一些效果,应该有一些参数设置不够完善。

    2.Python代码

    退火代码:

    def sa_tsp(points, T0, k):
        "模拟退火算法,T0为初温,k为内循环的次数"
        accumulator = []
        best_path = points[:] 
        best_length = calLength(points)
        old_length = best_length
        new_length = best_length
        old_path = points[:]
        new_path = []
        Tmin = 0.99
        T = T0
        best_time = 0
        #外循环
        while(T > Tmin):
            #内循环
            for i in range(k):
                new_path.clear()
                #产生新路径
                new_path = generate_newpath(old_path)
                #计算新路径的长度
                new_length = calLength(new_path)
                
                alpha = np.exp( (old_length-new_length)/T)
                #metropolis判断准则
                if (new_length < old_length) or np.random.uniform() < alpha:
                  #  print()
                    accumulator.append((T, new_length, new_path))
                    if new_length < best_length:
                        best_length = new_length
                        best_path = new_path[:]
                        best_time = T
                    #满足则保留当前状态
                    old_path = new_path[:] 
                   # print(len(old_path))
                    old_length = new_length
                else:
                    #keep the old stuff
                    #不满足则保留旧状态
                    accumulator.append((T, old_length, old_path))
            #温度更新函数
            T = 0.8 * T
        print_path(accumulator)
        return (best_path, best_length)

    全部代码

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    from time import time
    import math
    from collections import namedtuple
    import numpy as np
    import matplotlib.pyplot as plt
    
    Point = namedtuple("Point", ['index', 'x', 'y'])
    #画图函数
    def plot_lines(points, style='bo-'):
        "Plot lines to connect a series of points"
        plt.plot([p.x for p in points], [p.y for p in points], style)
        plt.axis('scaled')
        plt.axis('off')
    #画图函数
    def plot_tour(points):
        "Plot the cities as circles and the tour as lines between them."
        "Start city is red square"
        start = points[0]
        plot_lines(points+[start])
        plot_lines([start], 'rs')
        
    
    #计算两个城市的欧几里德距离
    def length(point1, point2):
        return math.sqrt((point1.x - point2.x)**2 + (point1.y - point2.y)**2)
    
    #计算整个旅程的距离
    def calLength(points):
        dist = 0
        dist += length(points[0], points[-1])
        for i in range(len(points)-1):
            dist += length(points[i], points[i+1])
        return dist
    #通过在[i,j]的元素逆序排放,产生邻域的方法1
    def reverse_segment(points, i, j):
        "Reverse segment points[i:j] of a tour "
        new_path = points[:]
        new_path[i:j] = reversed(points[i:j])
        return new_path
    #交换i,j位置的元素,产生邻域的方法2
    def swap_point(points, i, j):
        "swap two cities at index i and index j"
    
        point0 = points[i]
        point1 = points[j]
        new_points = points[:]
        #swap two points
        new_points[i] = point1
        new_points[j] = point0
        return new_points
    
    #选择某种得到邻域的方法
    change_point = reverse_segment
    
    #生成新路径
    def generate_newpath(input_points):
        "change a tour for tsp iteration"
        #假设起点位置始终是0,故只在[1,len(input_points)-1]的区间取随机数
        indices = range(1, len(input_points)) 
        #print("indices'length is {0}.".format(len(indices)))    
        #take two random indices to swap
        #生成两个随机数
        c1 = np.random.choice(indices)
        c2 = np.random.choice(indices)
        
        new_path = change_point(input_points, c1, c2)
        #print("new path'length is {0}.".format(len(new_path)))    
        return new_path
        
    def print_path(accumulator):
    
        f = open("path info", 'w+')
        #print("accumulator's length is {0}.".format(len(accumulator)))
        for i in range(len(accumulator)):
            print("{0}, {1}.".format(accumulator[i][0], accumulator[i][1]), file=f)
    
    def sa_tsp(points, T0, k):
        "模拟退火算法,T0为初温,k为内循环的次数"
        accumulator = []
        best_path = points[:] 
        best_length = calLength(points)
        old_length = best_length
        new_length = best_length
        old_path = points[:]
        new_path = []
        Tmin = 0.99
        T = T0
        best_time = 0
        #外循环
        while(T > Tmin):
            #内循环
            for i in range(k):
                new_path.clear()
                #产生新路径
                new_path = generate_newpath(old_path)
                #计算新路径的长度
                new_length = calLength(new_path)
                
                alpha = np.exp( (old_length-new_length)/T)
                #metropolis判断准则
                if (new_length < old_length) or np.random.uniform() < alpha:
                  #  print()
                    accumulator.append((T, new_length, new_path))
                    if new_length < best_length:
                        best_length = new_length
                        best_path = new_path[:]
                        best_time = T
                    #满足则保留当前状态
                    old_path = new_path[:] 
                   # print(len(old_path))
                    old_length = new_length
                else:
                    #keep the old stuff
                    #不满足则保留旧状态
                    accumulator.append((T, old_length, old_path))
            #温度更新函数
            T = 0.8 * T
        print_path(accumulator)
        return (best_path, best_length)
    
        
    
    
    
    
    def solve_it(input_data):
        # Modify this code to run your optimization algorithm
    
        # parse the input
        lines = input_data.split('
    ')
    
        nodeCount = int(lines[0])
        cnt = 0
        points = []
        for i in range(1, nodeCount+1):
            line = lines[i]
            parts = line.split()
            points.append(Point(cnt, float(parts[0]), float(parts[1])))
            cnt += 1
    
        # build a trivial solution
        # visit the nodes in the order they appear in the file
        
        solution = range(0, nodeCount)
        result_path,result_length = sa_tsp(points, 1000, 10000)
        #画图函数
        plot_tour(result_path)
        plt.show()
    
        solution2 = []
        #print("sa_tsp_result length is {0}".format(result_length))
        for point in result_path:
            #print(point.index, end=' ')
            solution2.append(point.index)
    
        #print() 
        #print("test the result: {0}.".format(calLength(result_path)))
        # calculate the length of the tour
    
        '''obj = length(points[solution[-1]], points[solution[0]])
        for index in range(0, nodeCount-1):
            obj += length(points[solution[index]], points[solution[index+1]])
        '''
        obj = result_length
        # prepare the solution in the specified output format
        output_data = '%.2f' % obj + ' ' + str(0) + '
    '
        output_data += ' '.join(map(str, solution2))
    
        return output_data
    
    
    import sys
    
    if __name__ == '__main__':
        import sys
        start = time()
        if len(sys.argv) > 1:
            file_location = sys.argv[1].strip()
            with open(file_location, 'r') as input_data_file:
                input_data = input_data_file.read()
            print(solve_it(input_data))
        else:
            print('This test requires an input file.  Please select one from the data directory. (i.e. python solver.py ./data/tsp_51_1)')
        end = time()
        print('running time is {0} s.'.format(end-start))
    View Code

    设置初温T0=100,内循环为k=100时的路线

    以及设置初温T0=1000,内循环为k=1000时的路线

    以及设置初温T0=1000,内循环为k=10000时的路线

    效果还不错,就是时间久了一点。

    The Safest Way to Get what you Want is to Try and Deserve What you Want.
  • 相关阅读:
    【牛客】找出单向链表中的一个节点,该节点到尾指针的距离为K
    【牛客】牛妹的礼物 (动态规划)
    【问题解决】Anaconda连不上服务器,"服务器正在启动,请稍等",报错信息:ImportError: cannot import name 'create_prompt_application'
    将一个数组的各元素插入到链表中,并以升序排列
    链式队列的相关操作
    循环队列的基本操作
    栈的相关操作
    双链表的基本操作
    单链表的相关操作
    顺序表的相关操作
  • 原文地址:https://www.cnblogs.com/Shinered/p/9806942.html
Copyright © 2011-2022 走看看