Python实现遗传算法(GA)解决(TSP)问题

纯个人学习记录,如有问题,请大佬指出。

'''以下是使用遗传算法求解TSP问题的 Python 代码示例:
该算法的思路是通过遗传算法不断地进行“繁殖”,从而得到更优秀的解。
它的基本流程分为初始化种群、适应度评估、选择、交叉和变异操作、更新种群和输出最优解等步骤。
在该代码中,输入数据包括城市数量 `city_number = 10 ` 和城市位置坐标 `city_position = []`,输出数据包括最短路径和最优解。
'''
'''
一、设置城市数量city_number,然后根据随机数生成城市的位置city_position,然后计算城市之间的距离矩阵dis_mat
city_number = 30  # 城市数
city_position = []  # 城市位置坐标/有横坐标和纵坐标[x,y]
for i in range(city_number):
    temporary = random.sample(range(100), 2)
    city_position.append(temporary)  # 城市位置坐标
print("城市坐标为:", city_position)



# 计算城市之间的距离矩阵
dis_mat = np.zeros((city_number, city_number))
for i in range(city_number):
    for j in range(city_number):
        if i != j:
            dis_mat[i][j] = math.sqrt((city_position[i][0] - city_position[j][0]) ** 2
                                      + (city_position[i][1] - city_position[j][1]) ** 2)
二、初始化种群,根据城市数量和种群个数来生成种群个数,此时种群个体表示的是路径
# 初始化种群并去重
def initpopulation(pop_size, city_number):
    popuation = []
    while len(popuation) < pop_size:
        temp = random.sample(range(city_number), city_number)
        if temp not in popuation:
            popuation.append(temp)
    return popuation
三、根据种群路径和城市之间的距离计算每条路径的适应值---方法:直根据路径进行城市距离的求和,然后取平方的倒数作为适应值、
def fitness(population, dis_mat):
    fitness = []
    for i in range(len(population)):
        distance = 0.0
        for j in range(city_number - 1):
            distance += dis_mat[population[i][j]][population[i][j + 1]]
        distance += dis_mat[population[i][-1]][population[i][0]]
        if distance == 0:
            f = float('inf')  # 如果路径长度为 0,则设置适应度为一个较大的数
        else:
            f = 1 / distance ** 2  # 计算路径长度平方的倒数作为适应度
        fitness.append(f)
    return fitness
四、进行选择操作---
    1. 首先,生成一个随机整数 index,这个整数的取值范围是从0到种群大小减1。
    2. 接着,初始化一个累加量 s,然后从头到尾遍历种群中的所有个体,依次将每个个体的适应度分值加到 s 中。
    3. 在每次循环中,判断当前 s 是否大于等于一个随机数 r,如果是,则返回该个体,否则继续循环。
    4. 返回选择出来的个体。

# 选择函数,轮盘赌选择
def select(population, fitness):
    index = random.randint(0, pop_size - 1)
    s = 0
    r = random.uniform(0, sum(fitness))
    for i in range(len(population)):
        s += fitness[i]
        if s >= r:
            index = i
            break
    # print(population[index])
    return population[index]

五、进行变异操作---
    1. 首先,判断是否进行变异操作。如果随机生成的一个小数值小于变异概率 pm,则进行变异操作,否则不进行任何操作。
    2. 然后,生成两个变异位置 mpoint1 和 mpoint2,这两个位置都是随机整数,并且取值范围是0到城市数减1。
    3. 最后,将变异位置上的两个基因位进行交换,从而生成一个新的个体。

def crossover(parent1, parent2):  # 传入两个父代
    if random.random() < pc:  # 按照一定的概率进行交叉操作
        chrom1 = parent1[:]  # 复制父代染色体
        chrom2 = parent2[:]
        # 交叉点,选择两个随机的交叉点。如果第一个点在第二个点的右侧,则交换两个点
        cpoint1 = random.randint(0, city_number - 1)
        cpoint2 = random.randint(0, city_number - 1)
        if cpoint1 > cpoint2:
            temp = cpoint1
            cpoint1 = cpoint2
            cpoint2 = temp

        # 未进行杂交之前,先保存两个父代个体的杂交段以及杂交段后面的片段
        # 保存cpoint1以及后面的片段
        temp1 = []
        temp2 = []
        for i in range(cpoint1, len(chrom1)):
            temp1.append(chrom1[i])
            temp2.append(chrom2[i])

        # 交叉操作,在交叉点之间对染色体进行交叉操作。
        for i in range(cpoint1, cpoint2 + 1):
            chrom1[i] = parent2[i]
            chrom2[i] = parent1[i]

        # 在杂交之后,先只保留每个父体杂交段以及杂交段以前的片段,然后在加上未杂交之前准备的杂交段以及杂交段后面的片段
        # 保存cpoint2以及前面的片段
        new_chrom1 = []
        new_chrom2 = []
        for i in range(cpoint2 + 1):
            new_chrom1.append(chrom1[i])
            new_chrom2.append(chrom2[i])
        new_chrom1.extend(temp1)
        new_chrom2.extend(temp2)

        # 现在通过映射的原理,去掉重复的城市点
        temporary1 = []
        temporary2 = []
        for i in range(len(new_chrom1)):
            if new_chrom1[i] not in temporary1:
                temporary1.append(new_chrom1[i])
        for i in range(len(new_chrom2)):
            if new_chrom2[i] not in temporary2:
                temporary2.append(new_chrom2[i])
        chrom1 = temporary1
        chrom2 = temporary2
        return chrom1, chrom2
    else:
        return parent1[:], parent2[:]  # 输出两个进行杂交操作后的两个个体
六、进行迭代计算
for iter in range(N):
    iter += 1
    fit_array = fitness(population, dis_mat)  # 适应值列表
    max_fitness = max(fit_array)
    max_index = fit_array.index(max_fitness)
    lx = []
    ly = []
    print(population[max_index][:])
    for i in population[max_index][:]:
        i = int(i)
        lx.append(city_position[i][0])
        ly.append(city_position[i][1])

    if max_fitness > best_fitness:
        best_fitness = max_fitness
        best_path = population[max_index][:]
        x = copy.copy(lx)
        y = copy.copy(ly)

    Best_Fitness.append(best_fitness)

    new_population = []
    n = 0
    while n < pop_size:
        p1 = select(population, fit_array)
        p2 = select(population, fit_array)
        while p2 == p1:
            p2 = select(population, fit_array)
        # 交叉
        chrom1, chrom2 = crossover(p1, p2)
        # 变异
        chrom1 = mutate(chrom1)
        chrom2 = mutate(chrom2)
        new_population.append(chrom1)
        new_population.append(chrom2)
        n += 2
    population = new_population
    print(f"第{iter}代群体为:", population)
    print("种群的适应值列表为:", fit_array)
    print("本代最优路径为:", best_path)
    print("本代最短路径长度为:", math.sqrt(1 / best_fitness))

    last_best_fitness = 0
    if last_best_fitness < math.sqrt(1 / best_fitness):
        last_best_fitness = math.sqrt(1 / best_fitness)
        last_best_path = best_path

    print("此时全局最优路径为:", best_path)
    print("此时全局最短路径长度为:", math.sqrt(1 / best_fitness))
    print("-------------------------------------------------" * 2)

print("最终全局最优路径为:", best_path)
print("最终全局最短路径长度为:", math.sqrt(1 / best_fitness))
七、绘出图像

def show(lx, ly, fit_history):
    # 画出每代最好适应值的图像
    plt.plot(range(len(fit_history)), fit_history)
    plt.xlabel("Generation")
    plt.ylabel("Fitness")
    plt.show()
    # 画出最短路径大小的变化图
    a = []
    for i in range(len(fit_history)):
        a.append(math.sqrt(1 / fit_history[i]))
    plt.plot(range(len(a)), a)
    plt.xlabel("Generation")
    plt.ylabel("Best_path_size")
    plt.show()


def best_show(x, y, Best_Fitness):
    # 定义两个子图
    fig, ax = plt.subplots(1, 2, figsize=(12, 5), facecolor='#ccddef')
    # 定义子图1标题
    ax[0].set_title("Best route")
    # 定义子图2标题
    ax[1].set_title("Best_Fitness Change Procession")
    # 画线
    ax[0].plot(x, y)
    # 画点(第一个子图)
    ax[0].scatter(x, y, color='r')
    # 画线(第二个子图)
    ax[1].plot(range(len(Best_Fitness)), [Best_Fitness[i] for i in range(len(Best_Fitness))])
    plt.show()
    pass

#################################################
'''

##########################

全部代码如下:

'''以下是使用遗传算法求解TSP问题的 Python 代码示例:
该算法的思路是通过遗传算法不断地进行“繁殖”,从而得到更优秀的解。
它的基本流程分为初始化种群、适应度评估、选择、交叉和变异操作、更新种群和输出最优解等步骤。
在该代码中,输入数据包括城市数量 `city_number = 10 ` 和城市位置坐标 `city_position = []`,输出数据包括最短路径和最优解。
'''
import copy

'''
一、设置城市数量city_number,然后根据随机数生成城市的位置city_position,然后计算城市之间的距离矩阵dis_mat
二、初始化种群,根据城市数量和种群个数来生成种群个数,此时种群个体表示的是路径
三、根据种群路径和城市之间的距离计算每条路径的适应值---方法:直根据路径进行城市距离的求和,然后取平方的倒数作为适应值、
四、进行选择操作---
    1. 首先,生成一个随机整数 index,这个整数的取值范围是从0到种群大小减1。
    2. 接着,初始化一个累加量 s,然后从头到尾遍历种群中的所有个体,依次将每个个体的适应度分值加到 s 中。
    3. 在每次循环中,判断当前 s 是否大于等于一个随机数 r,如果是,则返回该个体,否则继续循环。
    4. 返回选择出来的个体。
五、进行变异操作---
    1. 首先,判断是否进行变异操作。如果随机生成的一个小数值小于变异概率 pm,则进行变异操作,否则不进行任何操作。
    2. 然后,生成两个变异位置 mpoint1 和 mpoint2,这两个位置都是随机整数,并且取值范围是0到城市数减1。
    3. 最后,将变异位置上的两个基因位进行交换,从而生成一个新的个体。
六、进行迭代计算
'''
import random
import math
import numpy as np
import matplotlib.pyplot as plt

# 终止条件:最大迭代次数
N = 20000
# 遗传算法参数
pop_size = 100  # 种群数
pc = 0.8  # 交叉概率
pm = 0.05  # 突变概率
city_number = 30  # 城市数
city_position = []  # 城市位置坐标/有横坐标和纵坐标[x,y]
for i in range(city_number):
    temporary = random.sample(range(100), 2)
    city_position.append(temporary)  # 城市位置坐标
print("城市坐标为:", city_position)
# city_position = [random.sample(range(100), 2) for _ in range(city_number)]  # 城市位置坐标
# print(city_position)
'''使用 Python 的列表推导式和 random 库中的 sample 函数生成一个长度为 city_number 的列表,
其中每个元素是一个由两个随机整数组成的二元组,表示对应城市的位置坐标。range(100) 表示随机整数的范围是 0 到 99(不包含 100),
2 表示每个二元组有两个整数,_ 表示这个变量不会被使用,即只用来控制循环次数。
这行代码的目的是生成城市的位置坐标,用于计算城市之间的距离。'''

# 计算城市之间的距离矩阵,  dis_mat[i][j]表示第 i 个城市到第 j 个城市之间的距离。
# 计算城市之间的距离矩阵
dis_mat = np.zeros((city_number, city_number))
for i in range(city_number):
    for j in range(city_number):
        if i != j:
            dis_mat[i][j] = math.sqrt((city_position[i][0] - city_position[j][0]) ** 2
                                      + (city_position[i][1] - city_position[j][1]) ** 2)


# 初始化种群并去重
def initpopulation(pop_size, city_number):
    popuation = []
    while len(popuation) < pop_size:
        temp = random.sample(range(city_number), city_number)
        if temp not in popuation:
            popuation.append(temp)
    print(popuation)
    # print(len(popuation))
    return popuation


# populations = [[i for i in range(city_number)] for j in range(pop_size)]
# print(populations)


def fitness(population, dis_mat):
    fitness = []
    for i in range(len(population)):
        distance = 0.0
        for j in range(city_number - 1):
            distance += dis_mat[population[i][j]][population[i][j + 1]]
        distance += dis_mat[population[i][-1]][population[i][0]]
        if distance == 0:
            f = float('inf')  # 如果路径长度为 0,则设置适应度为一个较大的数
        else:
            f = 1 / distance ** 2  # 计算路径长度平方的倒数作为适应度
        fitness.append(f)

    return fitness


# 选择函数,轮盘赌选择
def select(population, fitness):
    index = random.randint(0, pop_size - 1)
    s = 0
    r = random.uniform(0, sum(fitness))
    for i in range(len(population)):
        s += fitness[i]
        if s >= r:
            index = i
            break
    # print(population[index])
    return population[index]


def crossover(parent1, parent2):  # 传入两个父代
    if random.random() < pc:  # 按照一定的概率进行交叉操作
        chrom1 = parent1[:]  # 复制父代染色体
        chrom2 = parent2[:]
        # 交叉点,选择两个随机的交叉点。如果第一个点在第二个点的右侧,则交换两个点
        cpoint1 = random.randint(0, city_number - 1)
        cpoint2 = random.randint(0, city_number - 1)
        if cpoint1 > cpoint2:
            temp = cpoint1
            cpoint1 = cpoint2
            cpoint2 = temp

        # 未进行杂交之前,先保存两个父代个体的杂交段以及杂交段后面的片段
        # 保存cpoint1以及后面的片段
        temp1 = []
        temp2 = []
        for i in range(cpoint1, len(chrom1)):
            temp1.append(chrom1[i])
            temp2.append(chrom2[i])

        # 交叉操作,在交叉点之间对染色体进行交叉操作。
        for i in range(cpoint1, cpoint2 + 1):
            chrom1[i] = parent2[i]
            chrom2[i] = parent1[i]

        # 在杂交之后,先只保留每个父体杂交段以及杂交段以前的片段,然后在加上未杂交之前准备的杂交段以及杂交段后面的片段
        # 保存cpoint2以及前面的片段
        new_chrom1 = []
        new_chrom2 = []
        for i in range(cpoint2 + 1):
            new_chrom1.append(chrom1[i])
            new_chrom2.append(chrom2[i])
        new_chrom1.extend(temp1)
        new_chrom2.extend(temp2)

        # 现在通过映射的原理,去掉重复的城市点
        temporary1 = []
        temporary2 = []
        for i in range(len(new_chrom1)):
            if new_chrom1[i] not in temporary1:
                temporary1.append(new_chrom1[i])
        for i in range(len(new_chrom2)):
            if new_chrom2[i] not in temporary2:
                temporary2.append(new_chrom2[i])
        chrom1 = temporary1
        chrom2 = temporary2
        return chrom1, chrom2
    else:
        return parent1[:], parent2[:]  # 输出两个进行杂交操作后的两个个体


def mutate(chrom):  # 变异函数
    if random.random() < pm:  # 按照一定的概率进行变异操作
        mpoint1 = random.randint(0, city_number - 1)  # 随机产生两个变异位置
        mpoint2 = random.randint(0, city_number - 1)
        # 交换变异点的基因位
        temp = chrom[mpoint1]
        chrom[mpoint1] = chrom[mpoint2]
        chrom[mpoint2] = temp
    return chrom


# 绘制适应度随迭代次数变化的曲线图
'''
利用 matplotlib 库绘制适应度随迭代次数变化的曲线图,以帮助观察算法的收敛情况。其中,fit_history 是一个列表,表示每一代中的最优
适应度值。代码中先通过 range(len(fit_history)) 生成一个迭代次数序列,然后以这个序列为 x 轴,将 fit_history 中保存的最优适应度值作为 y 轴,
绘制曲线图。这个曲线图可以直观地显示算法的收敛情况,如果适应度值随着迭代次数的增加而不断趋近于一个稳定值,则说明算法已经收敛到最优解;否则,需要进
一步优化算法的参数设置和策略。
函数 plt.xlabel 和 plt.ylabel 则设置 x 轴和 y 轴的标签,分别是 “Generation” 和 “Fitness”;函数 plt.show() 则将曲线图显示出来。'''


def show(lx, ly, fit_history):
    # 画出每代最好适应值的图像
    plt.plot(range(len(fit_history)), fit_history)
    plt.xlabel("Generation")
    plt.ylabel("Fitness")
    plt.show()
    # 画出最短路径大小的变化图
    a = []
    for i in range(len(fit_history)):
        a.append(math.sqrt(1 / fit_history[i]))
    plt.plot(range(len(a)), a)
    plt.xlabel("Generation")
    plt.ylabel("Best_path_size")
    plt.show()


def best_show(x, y, Best_Fitness):
    # 定义两个子图
    fig, ax = plt.subplots(1, 2, figsize=(12, 5), facecolor='#ccddef')
    # 定义子图1标题
    ax[0].set_title("Best route")
    # 定义子图2标题
    ax[1].set_title("Best_Fitness Change Procession")
    # 画线
    ax[0].plot(x, y)
    # 画点(第一个子图)
    ax[0].scatter(x, y, color='r')
    # 画线(第二个子图)
    ax[1].plot(range(len(Best_Fitness)), [Best_Fitness[i] for i in range(len(Best_Fitness))])
    plt.show()
    pass


# 主程序
best_path = []
best_fitness = 0.0
Best_Fitness = []  # 用来存放每代种群中最个体的适应值
population = initpopulation(pop_size, city_number)  # 初始种群
fit_array = fitness(population, dis_mat)
print("初始种群为:", population)
print("初始种群适应值列表:", fit_array)
print("城市之间的距离矩阵为:")
print(dis_mat)
print("########################")
# population = select(population, fit_array)

for iter in range(N):
    iter += 1
    fit_array = fitness(population, dis_mat)  # 适应值列表
    max_fitness = max(fit_array)
    max_index = fit_array.index(max_fitness)
    lx = []
    ly = []
    print(population[max_index][:])
    for i in population[max_index][:]:
        i = int(i)
        lx.append(city_position[i][0])
        ly.append(city_position[i][1])

    if max_fitness > best_fitness:
        best_fitness = max_fitness
        best_path = population[max_index][:]
        x = copy.copy(lx)
        y = copy.copy(ly)

    Best_Fitness.append(best_fitness)

    new_population = []
    n = 0
    while n < pop_size:
        p1 = select(population, fit_array)
        p2 = select(population, fit_array)
        while p2 == p1:
            p2 = select(population, fit_array)
        # 交叉
        chrom1, chrom2 = crossover(p1, p2)
        # 变异
        chrom1 = mutate(chrom1)
        chrom2 = mutate(chrom2)
        new_population.append(chrom1)
        new_population.append(chrom2)
        n += 2
    population = new_population
    print(f"第{iter}代群体为:", population)
    print("种群的适应值列表为:", fit_array)
    print("本代最优路径为:", best_path)
    print("本代最短路径长度为:", math.sqrt(1 / best_fitness))

    last_best_fitness = 0
    if last_best_fitness < math.sqrt(1 / best_fitness):
        last_best_fitness = math.sqrt(1 / best_fitness)
        last_best_path = best_path

    print("此时全局最优路径为:", best_path)
    print("此时全局最短路径长度为:", math.sqrt(1 / best_fitness))
    print("-------------------------------------------------" * 2)

print("最终全局最优路径为:", best_path)
print("最终全局最短路径长度为:", math.sqrt(1 / best_fitness))

show(lx, ly, Best_Fitness)
# 画出全局最短路径路径图  画出全局没代最好适应值变化图
x.append(x[0])
y.append(y[0])
best_show(x, y, Best_Fitness)

运行结果如下:

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
遗传算法是一种启发式优化算法,常用于求解TSP(Traveling Salesman Problem)问题。下面是使用遗传算法求解TSP问题Python代码示例: ```python import random # 定义TSP问题的距离矩阵 distance_matrix = [ [0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0] ] # 定义遗传算法的参数 population_size = 50 # 种群大小 elite_size = 10 # 精英个体数量 mutation_rate = 0.01 # 变异率 generations = 100 # 迭代次数 # 创建一个个体(路径) def create_individual(): individual = list(range(len(distance_matrix))) random.shuffle(individual) return individual # 创建初始种群 def create_population(): population = [] for _ in range(population_size): population.append(create_individual()) return population # 计算路径的总距离 def calculate_fitness(individual): total_distance = 0 for i in range(len(individual)): from_city = individual[i] to_city = individual[(i + 1) % len(individual)] total_distance += distance_matrix[from_city][to_city] return total_distance # 选择精英个体 def select_elite(population): population_with_fitness = [(individual, calculate_fitness(individual)) for individual in population] population_with_fitness.sort(key=lambda x: x[1]) return [individual for individual, _ in population_with_fitness[:elite_size]] # 交叉互换操作 def crossover(parent1, parent2): child = [None] * len(parent1) start_index = random.randint(0, len(parent1) - 1) end_index = random.randint(start_index + 1, len(parent1)) child[start_index:end_index] = parent1[start_index:end_index] for i in range(len(parent2)): if parent2[i] not in child: for j in range(len(child)): if child[j] is None: child[j] = parent2[i] break return child # 变异操作 def mutate(individual): for i in range(len(individual)): if random.random() < mutation_rate: j = random.randint(0, len(individual) - 1) individual[i], individual[j] = individual[j], individual[i] return individual # 进化过程 def evolve(population): elite = select_elite(population) new_population = elite[:] while len(new_population) < population_size: parent1 = random.choice(elite) parent2 = random.choice(elite) child = crossover(parent1, parent2) child = mutate(child) new_population.append(child) return new_population # 主函数 def tsp_ga(): population = create_population() for _ in range(generations): population = evolve(population) best_individual = min(population, key=calculate_fitness) best_distance = calculate_fitness(best_individual) return best_individual, best_distance # 执行遗传算法求解TSP问题 best_individual, best_distance = tsp_ga() print("Best individual:", best_individual) print("Best distance:", best_distance) ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值