数学建模-遗传算法(比赛使用版)

  • 记录一下数模比赛自己写出来的用于解题的遗传算法,我门组这次华为杯数模竞赛做的是F题,是有关航空公司机组人员航行规划的问题。
  • 我们根据本题设计了的基因序列是2310位的二进制数。我们将该题转化为了0,1问题,基因的前2205位代表机组飞行选择的具体方案,而后105位用于表示fitness,后面105位所含的1的个数越多,则说明该种方案越好。
import numpy as np
import heapq
import csv

DNA_SIZE = 2310  # 表示DNA的所转化的二进制数取长度为2310
POP_SIZE = 1000  # 表示种群中个体的数量
CROSS_RATE = 0.8  # 表示父母DNA进行交叉配对的时候的配对比率(百分之八十个体进行交叉配对,百分之二十不参加)
MUTATION_RATE = 0.03  # 表示变异的强度,在遍历DNA的每一位的时候有百分之0.3的概率将0变1(或者1变0)
N_GENERATIONS = 2000  # 表示种群一共繁衍多少代


# ExtractDNA(pop)函数用于将DNA后面判断是否可以正常起飞的数据进行提取,即DNA最后的105位二进制代码
def ExtractDNA(pop):
    take_off = []  # take_off存放DNA代码的最后105位 代表飞机是否可以正常起飞
    for i in range(len(pop)):  # pop是存放个体的数组,for循环表示有多少个个体就应该提取多少组DNA编码
        x = []
        for j in range(2205, DNA_SIZE):  # 提取每条DNA的第2205位到最后一位的105位DNA序列
            x.append(pop[i][j])  # 数组x用于保存一条DNA的后105位片段
        take_off.append(x)  # 将所有个体的最后105位基因片段全部放入take_off数组中
    return take_off


# get_fitness()函数用于计算个体对环境的适应度,适应度越高的个体越容易存活。
def get_fitness(pred):
    take_off_value = []  # 用于存放每个子代飞机可以正常起飞的总数量
    for i in range(len(Extract_values)):
        sum = 0  # sum用于计算一条DNA的最后105位二进制数之和 判断有多少航班可以正常飞行 sum越大说明可以正常飞行的航班越多 说明这个个体(执勤方案)越合适生存
        for j in range(105):  # 105次循环计算最后105位二进制数只和
            sum += Extract_values[i][j]
        take_off_value.append(sum)  # 将每条DNA最后105位二进制数之和放入take_off_value数组中
    return take_off_value


# select()函数用于淘汰掉完全不合适的子代,适者生存
def delete(pop):
    # 问题一的筛选约束:
    for i in range(1000):  # i代表子代个数
        flag = False
        for k in range(0, 104):  # k代表105个航班 105次判断
            sum = 0
            for j in range(k, 1155, 105):  # j用于遍历
                sum += pop[i][j]
                if sum >= pop[i, 2205 + k]:  # 满足就跳出循环
                    break
            if sum < pop[i, 2205 + k]:
                pop = np.delete(pop, i, axis=0)
                break

    # 在问题一的基础上添加问题二的筛选约束:需要用到事先制作的冲突矩阵
    for i in range(1000):  # 每个子代都要遍历到
        break_flag = False
        for j in range(21):  # 每个DNA取前21个片段进行遍历
            sum = 0
            if break_flag == True:
                break
            for k in range(105):  # 每个片段有105个数字
                if break_flag == True:
                    break
                for s in range(105):  # 每个片段乘以105列冲突矩阵
                    m = j * 105 + s
                    sum += pop[i][m] * int(Conflict_matrix[s][k])
                    if sum > 1:
                        break_flag = True
                        pop = np.delete(pop, i, axis=0)
                        break
    return pop


# select()函数用于筛选出基因较为优秀的子代,即航班正常飞行率高的子代
def select(fitness):
    fitness = np.array(fitness)
    idx = heapq.nlargest(1000, range(len(fitness)),
                         fitness.take)  # 选择前一千个fitness值较大的下标,fitness是之前计算的105个基因片段之和,fitness越大,说明子代越优秀
    return pop[idx]


# crossover()函数用于对父母的DNA进行交叉配对,生成新的子代
def crossover(parent, pop_copy):
    size = len(pop_copy)
    if np.random.rand() < CROSS_RATE:  # np.random.rand()表示返回一个从“0~1”均匀分布的随机样本值,CROSS_RATE表示前面设定的交叉配对比率
        i_ = np.random.randint(0, size, size=1)  # 从0~POP_SIZE随机选择一个数作为母亲的序号(选择母亲)
        a = np.random.randint(0, 21, size=1)  # 随机选取基因交换位置
        cross_point1 = a[0] * 105  # 基因交换的起始地址
        cross_point2 = cross_point1 + 105  # 基因交换的最后地址
        for i in range(cross_point1, cross_point2):  # 进行基因片段的交换
            parent[i] = pop_copy[i_, i]
    return parent


# mutate()函数用于对孩子的基因进行变异
def mutate(child):
    for i in range(2205):  # 遍历DNA前半部分进行随机变异(除去最后105个二进制数)
        if np.random.rand() < 0.05:  # np.random.rand()表示返回一个从“0~1”均匀分布的随机样本值,0.05是变异概率
            child[i] = 1 if child[i] == 0 else 0  # 将1变成0或将0变成1
    for i in range(2205, 2310):  # 对基因的后105位进行基因突变
        if np.random.rand() < 0.01:  # 0.01是变异的概率
            if np.random.rand() < 0.3:  # 让fitness编码变异成1的概率大于变0的概率,促使DNA尽量往较好的方向变异
                child[i] = 0
            else:
                child[i] = 1
    return child


# 导入需要的冲突矩阵
file = "/Users/yzyzzzz/Documents/2021研究生数学建模/2021年中国研究生数学建模竞赛赛题/2021年F题/冲突矩阵.csv"
with open(file, 'r', encoding='utf-8') as csvfile:
    csv_reader = csv.reader(csvfile)
    OriginalData = list(csv_reader)  # 把csv文件转换成list
    Conflict_matrix = OriginalData.copy()

# 随机生成子代
# 随机生成0-2之间的整数(不包括2),siz=(行数,列数),POP_SIZE个体数量,DNA_SIZE二进制数的长度
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))

for _ in range(N_GENERATIONS):
    pop_copy = pop.copy()  # 把pop当成最初的父代,拷贝pop生成母代
    children = []
    for x in range(len(pop)):  # 在种群中选择父代
        parent = pop[x]
        child = crossover(parent, pop_copy)  # 调用函数对父代和母代进行交叉配对,产生新的子代
        child = mutate(child)  # 调用函数对子代进行基因变异
        pop = np.vstack((pop, child))  # 将最终生成的子代放入总的pop种群中
    pop = delete(pop)  # 对目前的种群进行筛选
    Extract_values = ExtractDNA(pop)  # 对DNA最后的fitness编码进行提取
    fitness = get_fitness(Extract_values)  # fitness存放各个基因的适应度,就此函数目标而言,提取出来的数的和越大,即是适应度越高的
    pop = select(fitness)  # 调用个体筛选函数,保留适应度高的个体存入pop中
    # Extract_values = ExtractDNA(pop)  # 对筛选后的1000个基因的fitness编码进行提取
    # fitness = get_fitness(Extract_values)
    print("当前种群的适应度分别是:", fitness)
    print("循环代数是:", _ + 1001)

fitness = np.array(fitness)
best_fitness = heapq.nlargest(3, range(len(fitness)), fitness.take)  # 找出最好的三个基因
print("最高的fitness是:", fitness[best_fitness])
print("最好的DNA坐标是:", best_fitness)
best_DNA = pop[best_fitness]  # 提取最好的三个基因序列
# 将三个最好的基因序列写入文件保存
with open('/Users/yzyzzzz/Documents/2021研究生数学建模/2021年中国研究生数学建模竞赛赛题/2021年F题/最好的机组分配情况(1.A)2.csv', 'a',
          encoding='utf-8', newline='') as csvfile2:
    writer = csv.writer(csvfile2)
    writer.writerows(best_DNA)  # 写入数据

  • 程序截图:
    在这里插入图片描述
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值