3-opt方法解旅行商问题的三边选取方法

解旅行商问题,最简单有效的算法是k-opt方法。常见的是2-opt和3-opt,相关算法的介绍文章网上很多,这里不加赘述,只对三边选择方法进行说明。
三条边的选取,应当满足两个原则:
原则一:不允许相接;违反这条,无法成功求解。
原则二:如果随机选择,应对服从均匀分布。违反这条,新解的出现频率不同,搜索范围被缩小,不利于找寻全局最优解的目标。
本程序采用随机抽取3个数字[0,n-1]内的数字,分别作为三条边的起点,也就是离开城市。具体抽样方式:
方法一:直接使用随机函数抽三点,如不可行,则重抽。
具体做法是:随机抽[0,n-1]内的3个不重复的整数,如果发现有任意两个数的差小于2(意味着两条边前后相连),则判为不可行的组合,重新再抽一次。
此方法好处是简单,适用于问题规模不太小的情况,推荐用于30城以上。
缺点是如果城市太少,因为符合的概率太低,则程序会陷入不断的重抽,效率低。因为随机抽取3个整数是可行组合(即:互不相连)的概率为 n ( n − 4 ) ( n − 5 ) 6 ( n 3 ) = ( n − 4 ) ( n − 5 ) ( n − 1 ) ( n − 2 ) \frac{\frac{n(n-4)(n-5)}{6}}{\left(\begin{matrix}n\\3\end{matrix}\right)}=\frac{(n-4)(n-5)}{(n-1)(n-2)} (n3)6n(n4)(n5)=(n1)(n2)(n4)(n5),随城市增加而增加,当达30城时为80%。
如果问题规模大,重抽机会很小,效率高。

方法二:三边依次在所有允许的范围内按均匀分布的概率抽取。
具体做法:
第一条边起点loc1在[0,n-1]范围内随意抽。第二条边起点loc2在除[loc1-1,loc1+1]范围以外的n-3个点内随意抽。第三条边起点loc3的抽取要分两种情况:
如果|loc1-loc2|=2,则允许抽的点还有n-5个;如果|loc1-loc2|>2,则允许抽的点还有n-6个。两者相加一共有 n ( n − 4 ) ( n − 5 ) 6 \frac{n(n-4)(n-5)}{6} 6n(n4)(n5)种组合。
最后上代码。

import numpy as np
distance_matrix=np.load(r'distance_matrix.npy')
global city_num
city_num=len(distance_matrix)

def distance_total_func(route):
    return distance_matrix[route[np.arange(city_num)-1],route].sum()

def change3(route,a,b,c):#要求a<b<c
    route1=np.repeat(route.reshape((1,city_num)),7,axis=0)
    route1[0]=np.concatenate((route[:a+1],route[c:b:-1],route[a+1:b+1],route[c+1:]))
    route1[1]=np.concatenate((route[:a+1],route[b+1:c+1],route[b:a:-1],route[c+1:]))
    route1[2]=np.concatenate((route[:a+1],route[b:a:-1],route[c:b:-1],route[c+1:]))
    route1[3]=np.concatenate((route[:a+1],route[b+1:c+1],route[a+1:b+1],route[c+1:]))
    #后三种情况其实是2-opt
    route1[4,a+1:b+1]=route[b:a:-1]
    route1[5,b+1:c+1]=route[c:b:-1]
    route1[6,a+1:c+1]=route[c:a:-1]
    return route1

def LOC():#随机选取互不相连的三条边
    if city_num<30:
        loc1=np.random.randint(0,city_num-1)
        loc2=np.random.randint(loc1+2,loc1-2+city_num)%city_num
        if (loc2-loc1)%city_num==2:
            loc3=np.random.randint(loc2+2,loc2-4+city_num-6)%city_num
        elif (loc1-loc2)%city_num==2:
            loc3=np.random.randint(loc1+2,loc1-4+city_num-6)%city_num
        else:
            loc3=np.random.randint(0,city_num-7)
            if loc3<(loc1-loc2-3)%city_num:
                loc3=(loc3+loc2+2)%city_num
            else:
                loc3=(loc3+loc2+5)%city_num
        loc=[loc1,loc2,loc3]
        loc.sort()
        return loc
    else:
        loc=[0,0,0]
        while loc[1]-loc[0]<2 or loc[2]-loc[1]<2:
            loc=np.random.choice(np.arange(city_num-1),size=3,replace=False)
            loc=np.sort(loc)
        return loc.tolist()
    
neighbor=((city_num)*(city_num-4)*(city_num-5))//6#全部可行组合的范围
best_route=np.random.permutation(city_num)
best=distance_total_func(best_route)

i=0
while i<neighbor:#可自行改小,这是上限,
    loc=LOC()
    routes_this=change3(best_route,loc[0],loc[1],loc[2])
    result=np.apply_along_axis(distance_total_func,axis=1,arr=routes_this)
    if min(result)<best:#发现更优解
        best=min(result)
        best_route=routes_this[result.argmin()]
        #print(r"%.2f"%best)#迭代过程展示
        i=0#重新计数
    else:
        i+=1
print(r"best:%.2f"%best,best_route)
  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值