局部搜索:变邻域搜索(Variable Neighbourhood Search, VNS)解决TSP问题的python案例

变邻域搜索(Variable Neighbourhood Search, VNS)解决TSP问题

本文是之前禁忌算法解决TSP问题的延伸,工作也是在上一篇代码的基础上进行的,传送门:

局部搜索:禁忌搜索(Tabu Search)解决TSP问题的python案例

什么是变邻域

在启发式局部搜索算法中,需要通过某种方法产生当前解的邻域解(解的一个集合),以往的搜索算法产生邻域的方法只有一种,而且是固定的。顾名思义,变邻域搜索就是,通过多个不同的邻域方法产生不同的邻域解的集合。

Variable Neighbourhood Descent, VND

在介绍VNS之前,先介绍一下VND,Variable Neighbourhood Descent也是一种VNS的方法
在这里插入图片描述
在搜索的过程中,遍历不同邻居方法产生的解的集合,如果在某个集合中最优邻域解优于最优解,就使用该解生成新的不同邻域解的集合,从头开始遍历这些邻域解方法,反之,继续从下一个方法产生的邻域解集合中寻找,直到遍历完所有的解集合。

Basic Variable Neighbourhood Search,BVNS

基本的变邻域搜索的方法,一般是一下步骤:
在这里插入图片描述
主要就是三个步骤,Shaking,在邻域中产生随机解,Local Search,使用生成的随机解利用一种Local Search的方法产生一个解,Move or not,判断最优解和上一步得到的解的效果,决定是否移动到下一个邻域空间,还是从第一个邻域空间使用最优解重复上述过程。

General Variable Neighbourhood Search,GVNS

一般的变邻域搜索的方法是使用VND代替上图的Local Search的过程:
在这里插入图片描述
因为Local Search使用的是VND的方法,所以在算法中需要两个不同邻域空间的集合,在每个集合中,产生不同邻域解集合的方法可以是相同的。

产生邻域解集合的方法

直接上代码,三种方法

#随机交换两个城市,最多产生max_num个解
def find_neighbour_zero(path, weights, max_num):
    solution_neighbours = []
    for i in range(0, max_num):
        exchange = random.sample(range(1, len(path)-1), 2)
        temp_path = copy.deepcopy(path)
        temp_path[exchange[0]] = path[exchange[1]]
        temp_path[exchange[1]] = path[exchange[0]]

        if temp_path not in solution_neighbours:
            cost = fitness(temp_path, weights)
            solution_neighbours.append([temp_path, cost])
    return solution_neighbours

#随机翻转某个区间,产生最多max_num个邻居解
def find_neighbour_one(path, weights, max_num):
    solution_neighbours = []
    for i in range(0, max_num):
        # 随机选择两个端点, 不改变先后顺序
        endpoints = random.sample(range(1, len(path)-1), 2)
        endpoints.sort()
        temp_path = copy.deepcopy(path)
        temp_path[endpoints[0]:endpoints[1]] = list(reversed(temp_path[endpoints[0]:endpoints[1]]))
        if temp_path not in solution_neighbours:
            cost = fitness(temp_path, weights)
            solution_neighbours.append([temp_path, cost])
    return solution_neighbours

#随机找两个城市放到序列最前面,产生最多max_num个邻居解
def find_neighbour_two(path, weights, max_num):
    solution_neighbours = []
    for i in range(0, max_num):
        # 随机选择两个city, 不改变先后顺序
        endpoints = random.sample(range(1, len(path)-1), 2)
        endpoints.sort()
        temp_path = copy.deepcopy(path)
        temp_path.pop(endpoints[0])
        temp_path.pop(endpoints[1] - 1)
        temp_path.insert(1, path[endpoints[0]])
        temp_path.insert(2, path[endpoints[1]])
        if temp_path not in solution_neighbours:
            cost = fitness(temp_path, weights)
            solution_neighbours.append([temp_path, cost])
    return solution_neighbours

搜索过程

下面把上面的伪代码变成python

def variable_neighbourhood_search(edge_points, weights, iters, neighbour_num, neighbour_func_sets, k_max=3, l_max=3):

    #绘图部分
    pyplot.close()
    fig = pyplot.figure()
    path_fig = fig.add_subplot(1, 2, 1)
    cost_fig = fig.add_subplot(1, 2, 2)
    path_fig.axis("equal")
    path_fig.set_title('Best Path')
    cost_fig.set_title('Best Cost')
    cost_fig.set_xlabel('iterations')
    cost_fig.set_ylabel('fitness')
    pyplot.subplots_adjust(wspace= 0.5)
    pyplot.ion()#打开交互
    path_fig.scatter([i[0] for i in edge_points], [i[1] for i in edge_points], s=2 ,color='red')
    pyplot.pause(0.001)

    first_path = [i for i in range(0, len(weights))]
    first_path.append(0)
    best_solution = [first_path, fitness(first_path,weights)]

    cost_history = list()
    cost_history.append(best_solution[1])
    for it in range(0, iters):

        #更新绘图
        cost_fig.plot(cost_history, color='b')
        path_fig.plot([edge_points[p][0] for p in best_solution[0]], [edge_points[p][1] for p in best_solution[0]], color='b', linewidth=1)
        pyplot.pause(0.001)
        path_fig.lines.pop(0)

        k = 0
        while k < k_max:

            #shaking
            s_1 = random.sample(neighbour_func_sets[k](best_solution[0], weights, neighbour_num), 1)[0]

            #local_search return x_0
            x_0 = copy.deepcopy(s_1)
            l = 0
            while l < l_max:
                neighbour_solution = neighbour_func_sets[l](x_0[0], weights, neighbour_num)
                neighbour_solution.sort(key= lambda x: x[1])
                x_1 = neighbour_solution[0] #findbestsolution
                if x_1[1] < x_0[1]:
                    x_0 = x_1
                    l = 1
                else:
                    l = l + 1
            
            #move or not
            if x_0[1] < best_solution[1]:
                best_solution = x_0
                k = 1
            else:
                k = k + 1

        cost_history.append(best_solution[1])
        print("iterations: %d, best cost:%.2f"%(it, best_solution[1]))
    path_fig.plot([edge_points[p][0] for p in best_solution[0]], [edge_points[p][1] for p in best_solution[0]], color='b', linewidth=1)
    pyplot.savefig('result.jpg')
    return best_solution

上面代码就完全按照伪代码的思路写的,调用的话就直接:

neighbourhood_funcs = [find_neighbour_zero, find_neighbour_one, find_neighbour_two]
variable_neighbourhood_search(edge, weights, opt.iters, opt.num, neighbourhood_funcs)

neighbourhood_funcs 是个函数的集合,用来产生不同的邻域解集合的,代码的剩余部分和上一篇文章相同,再来个传送门:

局部搜索:禁忌搜索(Tabu Search)解决TSP问题的python案例

搜索过程的动图就不放了,看一下最终结果:
在这里插入图片描述

再和上一篇禁忌搜索的结果对比一下:
在这里插入图片描述

TIPS

在这里插入图片描述
之前我在哪里查的,说sample函数不改变元素的先后顺序,然而,他的确改变了。所以先排个序,在翻转区间的那个邻域函数中,必须小的在前面,不然翻转没有效果。
在这里插入图片描述
在这里插入图片描述
然后就是在删除这两个元素的代码中,删除第一元素后,第二个原本的索引就变了,所以要减一,不然你会发现,图里面有些city会重复或者不经过了,哀,调试了半天才发现这个问题…

至此,局部搜索的算法介绍就告一段落了,下面的研究内容是演化算法,不见不散。

致谢

【优化算法】变邻域搜索算法(VNS)求解TSP(附C++详细代码及注释)

(PDF) Variable Neighbourhood Search - ResearchGate

  • 14
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
抱歉,作为AI语言模型,我无法提供您所需的代码,但我可以为您提供邻域搜索算法TSP问题的思路。邻域搜索算法是一种启发式算法,用于求解旅行商问题(TSP)。其基本思路是在搜索过程中不断改邻域结构,以期达到更优的解。具体操作如下: 1. 初始化 随机生成一条初始路径,计算其总长度。 2. 邻域搜索 在当前路径的邻域搜索更优的解,即通过交换路径上的两个点、逆转路径或将一段路径插入到另一段路径中等方式,得到一个新的路径。如果新路径的总长度比当前路径短,则将其作为当前路径。否则,根据一定的概率接受该解,以避免陷入局部最优解。 3. 终止条件 当达到一定的迭代次数或算法运行时间超过预设阈值时,停止搜索并输出当前最优解。 通过以上步骤,可以得到一个近似最优解。这里提供一些Matlab代码供参考: % 生成初始路径 n = 10; % 城市数量 x = rand(n,1); % x坐标 y = rand(n,1); % y坐标 d = pdist([x,y]); % 计算距离矩阵 path = [1:n 1]; % 初始路径 dist = sum(d(sub2ind([n n],path(1:end-1),path(2:end)))); % 计算总长度 % 邻域搜索 maxiter = 1000; % 最大迭代次数 iter = 0; % 当前迭代次数 while iter < maxiter % 交换路径上的两个点 newpath = path; i = randi(n-1); j = randi(n-1); if i >= j, j = j+i; i = j-i; j = j-i; end % 保证i<j newpath(i+1:j) = path(j:-1:i+1); newdist = sum(d(sub2ind([n n],newpath(1:end-1),newpath(2:end)))); if newdist < dist path = newpath; dist = newdist; else delta = newdist - dist; p = exp(-delta/iter); % 以一定概率接受劣解 if rand < p, path = newpath; dist = newdist; end end iter = iter + 1; end % 输出结果 disp(['Total distance: ' num2str(dist)]); plot(x(path),y(path),'-o'); % 绘制路径图

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糖醋奶茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值