遗传算法解决旅行商问题(TSP)

问题:旅行商要去20个城市(本题中随机化每个城市的位置)贩卖货物,请问他走哪条路径路程最短?
解题步骤:
1、随机初始化20个城市的横纵坐标。
2、随机生成原始种群 p o p pop ,假设种群中含有500条染色体,每个染色体上含有20个基因代表城市。
3、从 p o p pop 中依次取出每条染色体,得到所有染色体上所有城市的横纵坐标。将横坐标放入 l x lx 中,将纵坐标放入 l y ly 中。( l x lx l y ly 都是 500 × 20 500\times20 的矩阵)
4、计算每条染色体规定的路线的旅行路程,并以此进行适应度计算。适应度大的在下一步被选到的概率大。
5、依概率从500条染色体中有放回地抽取500条染色体(有重复染色体),令这新的500条染色体组成新的种群 p o p n e w pop_{new}
6、将 p o p n e w pop_{new} 复制为 p o p c o p y pop_{copy} 来表示另一条父染色体用来与 p o p n e w pop_{new} 中的染色体进行交叉配对。
7、从 p o p n e w pop_{new} 中选出第一条染色体(称其为 f a t h e r father )进行交叉检验。
8、假设交叉的概率为 0.1 0.1 ,则 p o p n e w pop_{new} 中的每条染色体都有 10 10% 的概率需要交叉,假设 f a t h e r father 是要进行交叉的。(若未被选中则继续第二条染色体的交叉检验,直到遍历完 p o p n e w pop_{new} 中所有染色体)
9、随机在 p o p c o p y pop_{copy} 中选择一条染色体与 p o p n e w pop_{new} 中第一条染色体进行交叉,假设选中 p o p c o p y pop_{copy} 中第三条染色体(称其为 m o t h e r mother )。
9、进行交叉操作。选择 f a t h e r father 上需要改变的基因是哪些,假设为第1,6,8,10,15个基因,假设它们代表的城市是 0 4 0-4
10、将 f a t h e r father 上其他未被选中的基因(代表 5 19 5-19 )保存下来,称为 c i t y k e e p city_{keep} ,在 m o t h e r mother 中找到代表城市 0 4 0-4 的基因,称为 c i t y s w a p city_{swap} ,按顺序将 c i t y k e e p city_{keep} c i t y s w a p city_{swap} 进行组合,得到子染色体 c h i l d child
11、对 c h i l d child 上的每个基因进行变异检验,假设变异概率为0.02。
12、假设 c h i l d child 上第一个基因需要变异,则从 c h i l d child 上再随机选择一个基因(仍有可能为第一个基因),让这两个基因互换位置。遍历完 c h i l d child 上的所有基因之后,用 c h i l d child 代替 f a t h e r father
13、遍历完 p o p n e w pop_{new} 中的所有染色体之后,第一次迭代完成。
14、达到迭代次数要求后, p o p n e w pop_{new} 中的适应度最高的染色体为最优染色体,即最优路线。
以下为 p y t h o n python 代码:

import numpy as np

N_CITIES = 20  # DNA size
CROSS_RATE = 0.1
MUTATE_RATE = 0.02
POP_SIZE = 500
N_GENERATIONS = 100

def translateDNA(DNA, city_position):     # get cities' coord in order
    line_x = np.empty_like(DNA, dtype=np.float64)  # 返回一个和输入矩阵shape相同的array
    line_y = np.empty_like(DNA, dtype=np.float64)
    for i, d in enumerate(DNA):                    
        # i 是从0到499这500个数;d 是第 i 条染色体的所有基因,如
        # 0 [ 6 19  5  3 10 17  1  8 15  7 18  2 13 12  4 14  9 16 11  0]
        city_coord = city_position[d]  # 将第 i 条染色体上的所有基因(城市)对应的坐标取出,将横坐标放 city_coord 第一列,纵坐标放第二列
        line_x[i, :] = city_coord[:, 0]  # 将第 i 条染色体上的所有城市的横坐标取出并放到 line_x 的第 i 行上
        line_y[i, :] = city_coord[:, 1]  # 将第 i 条染色体上的所有城市的纵坐标取出并放到 line_y 的第 i 行上
    return line_x, line_y

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)):
        total_distance[i] = np.sum(np.sqrt(np.square(np.diff(xs)) + np.square(np.diff(ys))))  # 计算第i条染色体上的总距离
    fitness = np.exp(N_CITIES * 2 / total_distance)  # 扩大距离差距
    return fitness, total_distance

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(parent, pop):
    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)
        keep_city = parent[~cross_points]  # 将染色体中 cross_points 为 False 的基因取出
        # pop[i_].ravel() --> pop 种群中第 i_ 条染色体上的所有基因
        # np.isin(pop[i_].ravel(), keep_city, invert=True) --> invert=True 表示若 keep_city 中的基因与pop 种群中第 i_ 条染色体上的某基因相同
        # 则将此位置置 False
        swap_city = pop[i_, np.isin(pop[i_].ravel(), keep_city, invert=True)]  # 将在 i_ 染色体上 keep_city 中没有的基因取出
        parent[:] = np.concatenate((keep_city, swap_city))
    return parent

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

city_position = np.random.rand(N_CITIES, 2)

pop = np.vstack([np.random.permutation(N_CITIES) for _ in range(POP_SIZE)])

for generation in range(N_GENERATIONS):
    lx, ly = translateDNA(pop, city_position)
    fitness, total_distance = get_fitness(lx, ly)
    best_idx = np.argmax(fitness)
    print('Gen:', generation, '| best fit: %.2f' % fitness[best_idx], '| shortest distance: % .2f' % total_distance[best_idx],
          '| path:', pop[best_idx])

    pop = select(pop, fitness)
    pop_copy = pop.copy()
    for parent in pop:
        child = crossover(parent, pop_copy)
        child = mutant(child)
        parent = child
发布了36 篇原创文章 · 获赞 1 · 访问量 566

猜你喜欢

转载自blog.csdn.net/qq_36758914/article/details/103332821