简单代码实现遗传算法解决旅行商问题(TSP)的改进(python)

网上见过很多采用遗传算法解决旅行商问题(TSP,也译作:货郎担问题)的文章,本人经过实际尝试,发现大多效果不佳。为此本人经过思考,进行了如下几个改进:

1、充分利用Python的numpy库,尽量采用numpy数组运算,避免人工循环造成过多的硬件开销。

2、编码。一般认为TSP问题的解是一个环,可以直接采用经过的城市代码为编码。但是这就忽视了,同一个解因为出发城市的不同和行进方向的不同,可以有多种(最多为城市数的2倍)序列表示形式。遗传算法恰恰对是以码为基因,如此则会极大干扰进化进程。为此对每个解都要进行清洗:强制规定0号城市为出发点,1号城市位于前半程。这样,除了极端情况(城市数量为偶数,并且1号城市恰好位于半程,也仅有2种表示)以外,都能保证同一个解只有一种表达形式。

3、简单地采用 适应度=1/(本次解的总路程-已知最总短路程+很小的正的常数),即可实现动态自适应的适应度函数,避免适应度的区分度过小的问题。极大增加接近已知最优的解的选择概率。

4、使用简单的np函数,既实现了轮盘赌选择(淘汰),又实现了最优个体保留,还实现了外来基因引入(故意对种群的基因进行污染)。

5、采用某段路程逆行的变异方式。网上文章多见采用两点互换变异,本人实践发现效果并不理想,往往导致原来已经形成的良好基因片段被打破。

6、动态确定进化终止代数。如果在接近预先确定的终止代数的时候发现更好的解,则自动延长进化代数。(由于代码和算法已经优化,硬件开销完全能够承受)。

经上述优化后,代码运行效率高,计算结果好。计算dantzig42问题时,多次得到最优解699。而且,对于初始种群没有特别要求,即便人为给与一个已经全部为最大值(最劣)的种群,也不会过早地收敛到比较差的解。

需要说明:上述的改进,全部都是针对称旅行商问题(STSP)的,对于非对称旅行商问题(ATSP),上述第2点改进不能使用,第5点改进不建议使用。

顺便说一句:crossover翻译成“杂交”更为妥帖,因为这个词本来就有“杂交育种”的意思。

附代码:

import numpy as np

#准备工作
#读取城市矩阵,提前计算成numpy.array形式,并保存在distance_matrix.npy
distance_matrix=np.load(r'distance_matrix.npy')

global city_num#城市数为全局变量
city_num=len(distance_matrix)

#计算一个解的总路程函数
def distance_total(route):
    return distance_matrix[route[np.arange(-1,city_num-1)],route].sum()

#解的清洗:规定0在出发位,1在前半程,避免同一路径的不同形式之间相互干扰。对于ATSP问题,仅能规定出发点,不能规定方向。
def wash_route(route):
    one_loc=np.argwhere(route==1)[0,0]
    zero_loc=np.argwhere(route==0)[0,0]
    if (one_loc-zero_loc)%city_num<=city_num//2:
        return np.append(route[zero_loc:],route[:zero_loc])
    else:
        return np.append(route[zero_loc::-1],route[:zero_loc:-1])

#遗传算法
global population,best_route,best#种群规模、当前最佳路径及对应距离为全局变量
population=city_num+city_num%2#必须为偶数,推荐取值=city_num或+1
best_route=wash_route(np.random.permutation(city_num))#初始最佳路径,随机设定
best=distance_total(best_route)#初始最优路程

#随机产生一组解(初始种群)
def species_random(population1=population):
    species=wash_route(best_route)
    for i in range(population1-1):
        species=np.vstack([species,wash_route(np.random.permutation(city_num))])#隐含现行最优解在第一位置,适应度与此有关
    return species

#选择(淘汰)
def choice(species,result):
    fit=1/(result-best+1)#转化为适应度
    choice_probality=fit/sum(fit)
    chosen=species[np.random.choice(a=np.arange(population),size=population-3,p=choice_probality)]#选中的个体
    return np.vstack((wash_route(np.random.permutation(city_num)),chosen,species[result.argmin()],best_route))

#杂交
def crossover(species,n=22):
    species=np.vstack([np.hstack([species[::2,:n],species[1::2,n:]]),np.hstack([species[1::2,:n],species[::2,n:]])])#每一个与下一个进行杂交
    for route in species:
        np.place(route[1:n],np.ma.in1d(route[1:n],route[n:],assume_unique=True,invert=False),np.setdiff1d(np.arange(city_num),route,assume_unique=True))
    return species

#变异,按种群
def mutate(species,p=0.1):
    chosen=np.random.random(size=population)<p#选中的变异个体
    mut_locs=np.random.randint(1,city_num,size=2)#变异点位
    while mut_locs[0]==mut_locs[1]:
    	mut_locs=np.random.randint(1,city_num,size=2)#如果两点相同,重选	
    species[chosen,mut_locs[0]:mut_locs[1]+1]=species[chosen,mut_locs[1]:mut_locs[0]-1:-1]#两城之间路径逆转,解STSP问题时推荐使用,解ATSP问题时不建议使用
    return np.apply_along_axis(wash_route,1,species)

#主程序
species=species_random(population)#初始种群,随机产生
#开始运算
i,i_max=0,2000
while i<i_max:
    result=np.apply_along_axis(distance_total,axis=1,arr=species)#计算本种群各解的总路程
    if min(result)<best:#发现更优解
        best=min(result)
        best_route=species[result.argmin()]
        i_max=max(i_max,i+1000)#在后期发现更优解,增加迭代次数
        print(r"%.2f"%best,i+1)
    species=choice(species,result)#淘汰
    species=crossover(species,n=np.random.randint(1,city_num//2))#杂交
    species=mutate(species,p=0.3)#变异
    i+=1
print(r"%.2f"%best,best_route,i)
#不含绘图
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值