遗传算法及旅行商问题(TSP问题)Python实现

遗传算法(Genetic Algorithm,简称GA)是一类借鉴生物界的进化规律(适者生存,优胜劣汰遗传机制)演化而来的随机化搜索方法,由美国的J.Holland教授1975年首先提出。遗传算法是一种模拟达尔文的遗传选择和自然淘汰的生物进化过程的计算模型,通过模拟自然进化过程搜索最优解,它常用来解决多约束条件下的最优问题。

遗传算法的基本操作及步骤

初始化: 随机生成一个规模为N的种群,设置最大进化次数以及停止进化条件。
计算适应度:适应度被用来评价个体的质量,且适应度是唯一评判因子。计算种群中每个个体的适应度,得到最优秀的个体。
选择:选择是用来得到一些优秀的个体来产生下一代.选择算法的好坏至关重要,因为在一定程度上选择会影响种群的进化方向。常用的选择算法有:随机抽取、竞标赛选择以及轮盘赌模拟法等等。
交叉:交叉是两个个体繁衍下一代的过程,实际上是子代获取父亲和母亲的部分基因,即基因重组。常用的交叉方法有:单点交叉、多点交叉等。
变异:变异即模拟突变过程.通过变异,种群中个体变得多样化.但是变异是有一个概率的。
流程图如下:

旅行商问题(TSP问题)Python实现程序:

import numpy as np
import random
import matplotlib.pyplot as plt
from matplotlib.ticker import FormatStrFormatter
import operator
import time
def build_dist_mat(input_list):
    n = len(input_list[:,0])
    dist_mat = np.zeros([n, n])
    for i in range(n):
        for j in range(i + 1, n):
            d = input_list[i, :] - input_list[j, :]
            # 计算点积
            dist_mat[i, j] = np.dot(d, d)
            dist_mat[j, i] = dist_mat[i, j]
    return dist_mat



# SMB
def select_best_mutaion(s, distmat):
    s_res = [slide_mutation(s[:]), inversion_mutation(s[:]), irgibnnm_mutation(s[:], distmat)]
    res = [cal_distance(s_res[0], distmat), cal_distance(s_res[1], distmat), cal_distance(s_res[2], distmat)]

    min_index = res.index(min(res))

    return s_res[min_index]


# 滑动变异
def slide_mutation(s):
    a, b = get_two_randint(len(s))
    t = s[a]
    for i in range(a + 1, b + 1):
        s[i - 1] = s[i]
    s[b] = t
    return s


# 获得一个旅行路径的距离
def cal_distance(sequence, distmat):
    cost = 0
    for i in range(len(sequence)):
        cost += distmat[sequence[i - 1]][sequence[i]]
    return cost


# 倒置变异
def inversion_mutation(s):
    # 自己手写的2变换
    a, b = get_two_randint(len(s))
    for i in range(a, (a + b) // 2 + 1):
        s[i], s[b + a - i] = s[b + a - i], s[i]

    return s


# 返回(小,大)两个随机数
def get_two_randint(size):
    b = a = random.randint(0, size - 1)
    while a == b:
        b = random.randint(0, size - 1)

    if a > b:
        return b, a
    return a, b


# irgibnnm
def irgibnnm_mutation(s, distmat):
    a, b = get_two_randint(len(s))
    # 先inversion
    for i in range(a, (a + b) // 2 + 1):
        s[i], s[b + a - i] = s[b + a - i], s[i]

    # 再RGIBNNM
    b = (b + 1) % len(s)

    min = b - 1
    for i in range(len(s)):
        if i == b:
            continue
        if distmat[b][min] > distmat[b][i]:
            min = i
    s[b], s[min - 4] = s[min - 4], s[b]

    return s

# 初始化种群
def init_population(individual_size, population_size):
    population_init = []
    for i in range(population_size):
        l = list(range(individual_size))
        population_init.append(random.sample(l, individual_size))

    return population_init
# 排序,并且返回length长的population
def select_sorted_population(fitness, population, length,population_size):
    sort_dict = {}
    for i in range(len(population)):
        sort_dict[(fitness[i], 1 / fitness[i])] = i

    sorted_key = sorted(sort_dict.keys(), key=operator.itemgetter(0), reverse=True)

    sorted_index = [sort_dict[i] for i in sorted_key]
    sorted_population = [population[i] for i in sorted_index]

    return sorted_population[:length]

# 获取最好的数据
def get_elite(fitness, population):
    max_index = fitness.index(max(fitness))
    max_fitness = fitness[max_index]
    max_individual = population[max_index]

    return max_fitness, max_individual


def record(f):
    global record_distance
    # 经纬度转米的单位要乘以111000
    record_distance.append(1 / f * 111000)
#    return record_distance

# 轮赌盘选择算子
def selection(fitness, num):
    def select_one(fitness, fitness_sum):
        size = len(fitness)
        i = random.randint(0, size - 1)
        while True:
            if random.random() < fitness[i] / fitness_sum:
                return i
            else:
                i = (i + 1) % size

    res = set()
    fitness_sum = sum(fitness)
    while len(res) < num:
        t = select_one(fitness, fitness_sum)
        res.add(t)
    return res

# 获得一个旅行路径的距离
def get_distance(sequence):
    global distmat

    cost = 0
    for i in range(len(sequence)):
        cost += distmat[sequence[i - 1]][sequence[i]]
    return cost

# 计算适应值
def get_fitness(population):
    fitness = []

    for i in range(len(population)):
        fitness.append(1 / get_distance(population[i]))

    return fitness

# 杂交算子
def crossover(parent1, parent2,individual_size):
#    global individual_size

    a = random.randint(1, individual_size - 1)
    child1, child2 = parent1[:a], parent2[:a]
#    print(len(child1))
#    print(len(child2))
    for i in range(individual_size):
        if parent2[i] not in child1:
            child1.append(parent2[i])

        if parent1[i] not in child2:
            child2.append(parent1[i])

    return child1, child2

# 获得城市之间的距离矩阵
def get_distmat(M):
    length = M.shape[0]
    distmat = np.zeros((length, length))

    for i in range(length):
        for j in range(i + 1, length):
            distmat[i][j] = distmat[j][i] = np.linalg.norm(M[i] - M[j])
    return distmat

# 画图
def plot(sequnce):
    global record_distance, coordinates

    plt.figure(figsize=(15, 6))
    plt.subplot(121)

    plt.plot(record_distance)
    plt.ylabel('distance')
    plt.xlabel('iteration ')

    plt.subplot(122)

    x_list = []
    y_list = []
    for i in range(len(sequnce)):
        x_list.append(coordinates[sequnce[i]][1])
        y_list.append(coordinates[sequnce[i]][0])
    x_list.append(coordinates[sequnce[0]][1])
    y_list.append(coordinates[sequnce[0]][0])

    plt.plot(x_list, y_list, 'c-', label='Route')
    plt.plot(x_list, y_list, 'ro', label='Location')

    # 防止科学计数法
    ax = plt.gca()
    ax.xaxis.set_major_formatter(FormatStrFormatter('%.2f'))
    ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))

    plt.xlabel("Longitude")
    plt.ylabel("Latitude")
    plt.title("Tsp Route")
    plt.grid(True)
    plt.legend()
    plt.show()
    
# 准备数据
file = 'demo.csv'


city_num = 50
pos_dimension = 2
# 城市坐标
city_pos_list = np.random.rand(city_num,pos_dimension)
# 城市距离矩阵
coordinates = build_dist_mat(city_pos_list)
distmat = get_distmat(coordinates)
# 参数初始化
individual_size = coordinates.shape[0]
max_generation = 1500
population_size = 10
p_mutation = 0.2
record_distance = []

generation = 1

population_cur = init_population(individual_size, population_size)
fitness = get_fitness(population_cur)

time_start = time.time()

## 终止条件
while generation < max_generation:
#
#    # 父代最好的留1/4活下来
    population_next = select_sorted_population(fitness, population_cur, population_size // 4,population_size)
#
#    # 杂交
    for i in range(population_size):
        p1, p2 = selection(fitness, 2)
        print(p1,p2)
        child1, child2 = crossover(population_cur[p1], population_cur[p2],individual_size)
#
        # 变异
        if random.random() < p_mutation:
            child1 = select_best_mutaion(child1, distmat)
        if random.random() < p_mutation:
            child2 = select_best_mutaion(child2, distmat)
#
        population_next.append(child1)
        population_next.append(child2)
#
#    # 选出下一代的种群
    population_next = select_sorted_population(get_fitness(population_next), population_next, population_size, population_size)
#
#    # 找出精英记录下来
    pre_max_fitness, pre_max_individual = get_elite(fitness, population_cur)
    record(pre_max_fitness)
#
#    # 换代
    population_cur = population_next
    generation += 1
    # 更新fitness
    fitness = get_fitness(population_cur)

# 记录并画图
final_fitness, final_individual = get_elite(fitness, population_cur)
record(final_fitness)

time_end = time.time()
print('进化花费时间:', time_end - time_start)
print('最后的路径距离(m):',get_distance(final_individual) * 111000)

plot(final_individual)

  • 7
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
以下是Python实现遗传算法解决TSP旅行商问题的示例代码: ```python import random # 城市坐标 city_pos = [(60, 200), (180, 200), (80, 180), (140, 180), (20, 160), (100, 160), (200, 160), (140, 140), (40, 120), (100, 120), (180, 100), (60, 80), (120, 80), (180, 60), (20, 40), (100, 40), (200, 40), (20, 20), (60, 20), (160, 20)] # 种群大小 POP_SIZE = 500 # 城市数量 ITY_COUNT = len(city_pos) # 交叉概率 CROSS_RATE = 0.1 # 变异概率 MUTATION_RATE = 0.02 # 代数 N_GENERATIONS = 500 # 计算两个城市之间的距离 def distance(city1, city2): return ((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2) ** 0.5 # 计算一条路径的总距离 def get_fitness(path): distance_sum = 0 for i in range(CITY_COUNT - 1): distance_sum += distance(city_pos[path[i]], city_pos[path[i+1]]) distance_sum += distance(city_pos[path[-1]], city_pos[path[0]]) return 1 / distance_sum # 初始化种群 def init_population(): population = [] for i in range(POP_SIZE): path = list(range(CITY_COUNT)) random.shuffle(path) population.append(path) return population # 选择 def select(population, fitness): idx = random.randint(0, POP_SIZE - 1) for i in range(POP_SIZE): if random.random() < fitness[i] / fitness.sum(): idx = i break return population[idx] # 交叉 def crossover(parent1, parent2): if random.random() < CROSS_RATE: child = [-1] * CITY_COUNT start = random.randint(0, CITY_COUNT - 1) end = random.randint(start, CITY_COUNT - 1) child[start:end+1] = parent1[start:end+1] for i in range(CITY_COUNT): if parent2[i] not in child: for j in range(CITY_COUNT): if child[j] == -1: child[j] = parent2[i] break return child else: return parent1 # 变异 def mutate(child): if random.random() < MUTATION_RATE: idx1, idx2 = random.sample(range(CITY_COUNT), 2) child[idx1], child[idx2] = child[idx2], child[idx1] return child # 遗传算法主函数 def genetic_algorithm(): population = init_population() for generation in range(N_GENERATIONS): fitness = [get_fitness(path) for path in population] best_path = population[fitness.index(max(fitness))] print("Generation:", generation, "| Best path length:", 1 / max(fitness)) new_population = [best_path] for i in range(POP_SIZE - 1): parent1 = select(population, fitness) parent2 = select(population, fitness) child = crossover(parent1, parent2) child = mutate(child) new_population.append(child) population = new_population return best_path # 运行遗传算法 best_path = genetic_algorithm() print("Best path:", best_path) ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

浪荡子爱自由

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

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

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

打赏作者

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

抵扣说明:

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

余额充值