遗传算法初学-旅行商问题

文章目录


参考:
话不多说,直接上代码

代码部分
#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author: liujie
@software: PyCharm
@file: TSP.py
@time: 2020/11/17 15:47
"""
'''
    问题是:旅行商要去20个城市(本题中随机化每个城市的位置)贩卖货物,请问他走哪条路径最短?
    解题步骤:
        1.随机初始化20个城市的横纵坐标
        2.随机生成原始种群pop,假设种群中含有500条染色体,每个染色体上含有20个基因代表城市
        3.从pop中依次选取每条染色体,得到所有染色体上所有城市的横纵坐标。将横坐标放入lx中,将纵坐标放入ly中(500*20)
        4.计算每条染色体规定的路线的旅行路程,并以此进行适应度计算,适应度大的在下一步被选到的概率大
        5.依概率从500条染色体中有放回地抽取500条染色体(有重复染色体),令这新的500条染色体组成新的种群pop_new
        6.将pop_new复制为pop_copy来表示另一条父染色体用来与pop_new中的染色体进行交叉配对
        7.从pop_new中选出第一条染色体(father)进行交叉检验
        8.假设交叉的概率为0.1,则pop_new中的每条染色体都有10%的概率需要交叉,假设father是要进行交叉的(若未被选中则继续第二
            条染色体的交叉检验,直至遍历完pop_new中所有染色体)
        9.随机在pop_copy中选择一条染色体与pop_new中的第一条染色体进行交叉,假设选中pop_copy中第三条染色体(mother)
        10.进行交叉操作。选择father上需要改变的基因有哪些呢,假设为第1,6,8,10,15个基因,假设他们代表的城市是0-4
        11.将father上其他未选中的基因(5-19)保存下来,称为city_keep,在mother中找到代表城市0-4的基因,称为city_swap,
            按顺序将city_keep和city_swap进行组合,得到子染色体child
        12.对child上的每个基因进行变异检验,假设变异概率是0.02
        13.假设child上的第一个基因需要变异,则从child上在随机选择一个基因(仍有可能是第一个基因),这两个基因互换位置。遍历完
            child上的所有基因后,用child代替father
        14.遍历完pop_new中的所偶遇染色体后,第一次迭代完成
        15.达到迭代次数后,pop_new中适应度最高的染色体为最优染色体,即最优路线
'''


import numpy as np

N_CITIES = 20 # DNA size
POP_SIZE = 500
CROSS_RATE = 0.1    # 交叉概率0.1
MUTATE_RATE = 0.02  # 变异概率0.02
N_GENERATIONS = 100 # 迭代次数

# 得到所有城市的坐标
def translateDNA(DNA,city_position):

    # np.empty_like-返回一个和输入矩阵shape相同的array
    line_x = np.empty_like(DNA,dtype=np.float64)
    line_y = np.empty_like(DNA,dtype=np.float64)
    for i,d in enumerate(DNA):
        # i 是从0到499这500个数;d是第i条染色体的所有基因
        # 将第i条染色体上的所有基因(城市)对应的坐标取出,将横坐标放入city——coord第一列
        city_coord = city_position[d]
        # 将第i条染色体上的所有城市的横坐标取出并放到line_x的第i行上
        line_x[i,:] = city_coord[:,0]
        line_y[i,:] = city_coord[:,1]

    return line_x,line_y


# 得到适应度,fitness = np.exp(N_CITIES * 2 / total_distance)
def get_fitness(line_x,line_y):
    total_distance = np.empty((line_x.shape[0],),dtype=np.float64)
    for i,(xs,ys) in enumerate(zip(line_x,line_y)):
        # 计算第i条染色体上的总距离
        total_distance[i] = np.sum(np.sqrt(np.square(np.diff(xs)) + np.square(np.diff(xs))))
    # 扩大距离差距
    fitness = np.exp(N_CITIES * 2 / total_distance)
    return fitness,total_distance

# 依概率从500条染色体中有放回地抽取500条染色体(有重复染色体)
def select(pop,fitness):

    idx = np.random.choice(np.arange(POP_SIZE),size=POP_SIZE,replace=True,p = fitness/fitness.sum())
    return pop[idx]

# 交叉验证
def crossover(father,pop):
    # np.random.rand()生成一个随机数(0-1内的浮点数)
    # np.random.randint() 生成一个整型随机数
    if np.random.rand() < CROSS_RATE:
        i_ = np.random.randint(0,N_CITIES,size=1)
        cross_points = np.random.randint(0,2,size=N_CITIES).astype(np.bool)
        # 将染色体中cross_points为False的基因取出
        keep_city = father[~cross_points]   # 未选中的基因
        # 种群中第i_条染色体上的所有基因
        # print(pop[i_].ravel())
        # np.isin()invert=False表示返回一样的bool索引,为True时表示返回不一样的bool索引
        # print(np.isin(pop[i_].ravel(),keep_city,invert=True))

        # 将i_染色体上keep_city中没有的基因取出
        swap_city = pop[i_,np.isin(pop[i_].ravel(),keep_city,invert=True)]
        father[:] = np.concatenate((keep_city,swap_city))   # 默认第一个轴

    return father


# 基因变异检验
def mutant(child):

    for point in range(N_CITIES):
        if np.random.rand() < MUTATE_RATE:
            swap_point = np.random.randint(0,N_CITIES,size=1)
            swapA,swapB = child[point],child[swap_point]
            child[point],child[swap_point] = swapB,swapA    # 基因位置互换
    return child

if __name__ == '__main__':
    # 随机化城市坐标
    city_position = np.random.rand(N_CITIES,2)  # (20,2)
    # 随机生成种群pop
    # np.vstack()按垂直方向堆叠数组
    pop = np.vstack([np.random.permutation((N_CITIES)) for _ in range(POP_SIZE)])   # (500,20)

    for generation in range(N_GENERATIONS):
        # 得到所有城市坐标x,y
        lx,ly = translateDNA(pop,city_position)
        # 得到适应度
        fitness,total_distance = get_fitness(lx,ly)
        # 找出适应度最大的索引,表明下一步选到概率大
        best_idx = np.argmax(fitness)
        # 打印现在情况
        print('Gen:',generation,'| best fitness: %.2f'% fitness[best_idx],'| shortest distance:%.2f'%total_distance[best_idx],
              '| path:',pop[best_idx])
        # 依概率从500条染色体中有放回地抽取500条染色体(有重复染色体)
        pop = select(pop,fitness)
        # 将新的种群复制为pop_copy
        pop_copy = pop.copy()
        # 遍历完pop中的所有染色体后,第一次迭代完成
        for father in pop:
            # 交叉操作得到子染色体
            child = crossover(father,pop_copy)
            # 变异检验
            child = mutant(child)
            father = child
结果部分
Gen: 0 | best fitness: 2297.22 | shortest distance:5.17 | path: [14 17  4  5  9  8 10 11 19 18  0  7  3  1 13 12 16  2  6 15]
Gen: 1 | best fitness: 2297.22 | shortest distance:5.17 | path: [14 17  4  5  9  8 10 11 19 18  0  7  3  1 13 12 16  2  6 15]
Gen: 2 | best fitness: 9761.97 | shortest distance:4.35 | path: [14 17  4  5  0  8 10 11 19 18  7  9  3  1 13 12 16  2  6 15]
Gen: 3 | best fitness: 9761.97 | shortest distance:4.35 | path: [14 17  4  5  0  8 10 11 19 18  7  9  3  1 13 12 16  2  6 15]
Gen: 4 | best fitness: 18836.41 | shortest distance:4.06 | path: [14 17  4  5  0  8 10  7 19 18 11  9  3  1 13 12 16  2  6 15]
Gen: 5 | best fitness: 1336357.58 | shortest distance:2.84 | path: [14 17  4  5  0  8 10 11  9 18  7 19  3  1 13 12 16  2  6 15]
Gen: 6 | best fitness: 2934291.05 | shortest distance:2.69 | path: [14 17  4  5  0  8 10 11  9 18  7 19  3  1 13 12 16 15  6  2]
Gen: 7 
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值