python 遗传算法 均匀散点问题

1. 算法简介

        利用遗传算法将N个点均匀散布到一个正方形区域内,尽可能充分均匀地覆盖整个平面区域,包括边界。其中N≥5,测试N=5,6,8,9,12,15,16,18,20的情况。
        注:独立编码,未使用遗传算法工具箱

2. 运行环境

        编码语言:Python
        集成开发环境:PyCharm Community Edition 2020.3.3 x64
        解释器:Anaconda-Python 3.8
        处理器:Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
        机带RAM:16.0 GB

3. 使用说明

        1、直接运行全部代码,按照提示"请输入点的数量:"输入点数N,待程序运行后即可得到优化后的结果及可视化散点图。
        2、以下五个参数均可调整:

参数含义取值范围
POP_SIZE种群数量参考遗传代数调整大小,一般取10-1000
CROSSOVER_RATE交叉率一般取0.8
MUTATION_RATE变异率一般取0.1
GENERATIONS遗传代数参考种群数量调整大小,一般取500-50000
b变异程度因子(0,1),取值越大,遗传后期变异程度越小

        3、相同点数N时,适应度best_fi tness越大,说明结果越接近最优解。

4. 结构说明

(1)解的表示

        采用[0,1000]内的正整数表示每一个坐标点的数值,故通过等比例放缩,可以得到单位正方 形平面内小数点后3位的精度。
        利用numpy库中的random函数生成所需的种群,如下

population = np.random.randint(0, 1000, size=(POP_SIZE, N*2))

        例如,当

N = 6
POP_SIZE = 5

        随机生成的种群为

[[273, 557, 707, 365, 231, 392, 857, 937, 165, 996, 85, 797], 
 [54, 188, 291, 146, 822, 108, 221, 419, 334, 218, 475, 714], 
 [673, 622, 194, 451, 904, 170, 977, 208, 918, 960, 803, 52], 
 [803, 893, 978, 266, 460, 762, 384, 449, 620, 831, 991, 936], 
 [8, 518, 248, 807, 118, 592, 201, 538, 537, 516, 838, 570]]

        其中每行表示一个个体,每个个体有2*N的坐标点,N个坐标对,第[2i]个坐标点和第[2i+1]个坐标点为一组坐标对,i∈[0,N]。

(2)交叉、变异算子

        交叉算子Crossover_and_Mutation有三个参数,分别为当前种群、交叉率、当前遗传代数

def Crossover_and_Mutation(pop, CROSSOVER_RATE, gen):
    for i in range(len(pop)):#遍历种群中的每一个个体,将该个体作为父体
        选择一个父体
        if np.random.rand() < CROSSOVER_RATE:#以CROSSOVER_RATE的概率交叉
            种群中选择另一个个体,并将该个体作为母体
            随机产生交叉的系数
            执行交叉计算
            population.append(Mutation(子代, MUTATION_RATE, gen))
            #交叉算子内嵌变异

        变异算子Crossover_and_Mutation有三个参数,分别为子代、变异率、当前遗传代数

def Mutation(子代, MUTATION_RATE, gen):#非均匀变异
    if np.random.rand() < MUTATION_RATE:#以MUTATION_RATE的概率变异
        随机产生变异的系数
        执行变异计算
        对变异结果进行约束条件限制
    return 变异后的个体

        变异采用了实数编码的非均匀变异

 其中:τ取值-1或1, 且取每个值的概率为50%;
        r是[0, 1]之间的随机数;
        tmax是最大遗传代数;
        t是当前遗传代数;
        b是一个可调参数。

(3)适应度函数

        适应度函数get_fi tness有两个参数,分别为个体、当前遗传代数

def cal_fitness(ind, gen):#个体适应度计算函数
    for i in range(N):
        for j in range(N-1, i, -1):
            计算两点间的距离fit保留最小距离
        return 最小距离

def get_fitness(pop, gen):#适应度计算函数
    for i in range(len(pop)):
        计算该个体适应度
        将该个体适应度及DNA存入字典
        将该个体适应度存入fitness列表
    return fitness列表

(4)选择算子

        适应度函数select有一个参数,为fi tness列表

def select(fit):#选择函数
    for i in range(len(fit_temp)):
        适应度求和
    for i in range(len(fit_temp)):
        得到单个个体被选择概率
    
    #轮盘赌
    #按概率选择
    #生成选择后的种群
    
    #锦标赛
    fitness列表降序排序
    取前POP_SIZE个个体
    生成选择后的种群
    
    return 选择后的种群

(5)迭代运行

start = time.time()#程序开始计时
    for i in range(GENERATIONS):#种群迭代进化GENERATIONS代
        Crossover_and_Mutation(population, CROSSOVER_RATE, i)
        #种群通过交叉变异产生后代
        fitness = get_fitness(population, i)[:]
        #对种群中的每个个体进行评估
        population = select(fitness)[:]
        #选择生成新的种群
        
        输出进度
        获取最大适应度个体,并保存

(6)结果输出及可视化

print("best_fitness:",best_fitness)#输出最佳适应度
for i in range(N):#绘制结果散点图
    plt.scatter(x[2*i], x[2*i+1])
end = time.time()#程序结束计时
输出参数POP_SIZE,CROSSOVER_RATE,MUTATION_RATE,GENERATIONS
输出程序运行时间

5. 常见问题说明

        1、源代码提供了实数编码和二进制编码两个版本,参考实际运行结果,实数编码的运行速度赶快,优化结果更加,故此代码说明均以实数编码的程序为例展开。
        2、由于平方数个点的最优状态的收敛域较窄,使得9和16个点较难以很高的概率收敛到最优;同样由于算法的随机性,复现运行结果时会出现适应度大于或小于附录中运行结果的情况。

6. 声明

        源代码链接 https://download.csdn.net/download/weixin_48365310/87587721

        全部程序均独立编码,算法框架参考自博客:
        遗传算法详解 附python代码实现 https://blog.csdn.net/ha_ha_ha233/article/details/91364937

        仅供学习交流,代码风格较差,望勿借鉴

附:运行结果

N=5

best_fi tness: 707.108
POP_SIZE = 20
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 10000
Running time: 29.826 seconds

N=6

best_fi tness: 592.444
POP_SIZE = 500
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 500
Running time: 152.439 seconds

 N=8

best_fi tness: 501.092
POP_SIZE = 20
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 10000
Running time: 64.601 seconds

N=9

best_fi tness: 477.010
POP_SIZE = 500
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 500
Running time: 115.770 seconds

N=12

best_fi tness: 349.997
POP_SIZE = 10
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 20000
Running time: 132.948 seconds

N=15

best_fi tness: 295.223
POP_SIZE = 10
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 20000
Running time: 195.829 seconds

N=16

best_fi tness: 291.001
POP_SIZE = 20
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 30000
Running time: 654.283 seconds

N=18

best_fi tness: 257.236
POP_SIZE = 20
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 20000
Running time: 565.277 seconds

N=20

best_fi tness: 247.537
POP_SIZE = 10
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 10000
Running time: 202.108 seconds

源代码

import numpy as np
import matplotlib.pyplot as plt
import copy
import time

POP_SIZE = 20     #种群数量
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 10000
b = 0.7        #变异程度因子


N = int(input("请输入点的数量:"))

population = np.random.randint(0, 1000, size=(1000, N*2))
population = population.tolist()

fitness = [0 for i in range(POP_SIZE)]  #初始化fitness列表
dic = {}  #初始化字典


def Crossover_and_Mutation(pop, CROSSOVER_RATE, gen):
    for i in range(len(pop)):  # 遍历种群中的每一个个体,将该个体作为父体
        father = pop[i]
        if np.random.rand() < CROSSOVER_RATE:  # 以CROSSOVER_RATE的概率交叉
            ch1 = np.zeros(2 * N, dtype='i')
            ch2 = np.zeros(2 * N, dtype='i')

            mother = pop[np.random.randint(POP_SIZE)]  # 在种群中选择另一个个体,并将该个体作为母体
            cross_position = np.random.randint(0, N * 2)  # 随机产生交叉的点

            for j in range(2 * N - cross_position):
                alpha = np.random.rand()
                ch1[cross_position+j] = alpha * mother[cross_position+j] + (1 - alpha) * father[cross_position+j]
                # 孩子得到位于交叉点后的母亲的基因
                if ch1[j] > 1000:
                    ch1[j] = 1000
                elif ch1[j] < 0:
                    ch1[j] = 0
                ch2[j] = alpha * father[j] + (1 - alpha) * mother[j]
                # 孩子得到位于交叉点后的母亲的基因
                if ch2[j] > 1000:
                    ch2[j] = 1000
                elif ch2[j] < 0:
                    ch2[j] = 0
            population.append(Mutation(ch1, MUTATION_RATE, gen))#变异
            population.append(Mutation(ch2, MUTATION_RATE, gen))#变异


def Mutation(seg, MUTATION_RATE, gen):#非均匀变异策略
    if np.random.rand() < MUTATION_RATE:  # 以MUTATION_RATE的概率变异
        mutate_position = np.random.randint(2 * N)  # 随机产生一个实数,代表要变异基因的位置
        tau = np.random.choice([-1, 1], size=1, p=[0.5, 0.5])
        seg[mutate_position] = seg[mutate_position] + tau[0] * 1000 * (
                    1 - pow(np.random.rand(), pow((1 - gen / GENERATIONS), b)))
        if seg[mutate_position] > 1000:   #约束最大值
            seg[mutate_position] = 1000
        elif seg[mutate_position] < 0:    #约束最小值
            seg[mutate_position] = 0
    return seg


def cal_fitness(ind, gen):  # 个体适应度计算函数
    fit = 0
    minn = 10000  # 任意大数
    '''if gen <= N*500:  #gen % int((N-1)/2) == 0:
        for i in range(N):
            for j in range(N - 1, i, -1):
                fit += 1 / (np.sqrt(pow(ind[2 * i] - ind[2 * j], 2) + pow(ind[2 * i + 1] - ind[2 * j + 1], 2)) + 1e-3)  # 能量计算
        return 1 / fit
    else:'''
    for i in range(N):#N个点之间最小距离作为适应度
        for j in range(N - 1, i, -1):
            fit = np.sqrt(
                pow(ind[2 * i] - ind[2 * j], 2) + pow(ind[2 * i + 1] - ind[2 * j + 1], 2)) + 1e-3  # 最大的最小值
            if fit < minn:
                minn = fit
    return minn


def get_fitness(pop, gen):  # 适应度计算函数
    fitness_value = 0
    fit = []
    for i in range(len(pop)):
        fitness_value = cal_fitness(pop[i], gen)  # 计算该个体适应度
        dic[fitness_value] = pop[i]  # 将该个体适应度及DNA存入字典
        fit.append(fitness_value)  # 将该个体适应度存入fitness列表
    return fit


def select(fit):  # 选择函数
    sum = 0
    fit_temp = fit[:]

    for i in range(len(fit_temp)):  # 适应度求和
        sum += fit_temp[i]
    for i in range(len(fit_temp)):  # 选择概率
        fit_temp[i] = fit_temp[i] / sum

    # 轮盘赌
    #pop_temp = np.random.choice(fit, size = POP_SIZE, replace = True, p = fit_temp)#按概率选择
    #pop_temp.tolist()
    #for i in range(POP_SIZE):   #生成选择后的种群
    #   pop_temp[i] = copy.deepcopy(dic[pop_temp[i]])

    # 锦标赛
    fit.sort(reverse=True)
    pop_temp = population[0:POP_SIZE]
    #pop_temp.tolist()
    for i in range(POP_SIZE):  # 生成选择后的种群
        pop_temp[i] = dic[fit[i]]

    return pop_temp


start = time.time()#开始计时
best_fitness = 0

for i in range(GENERATIONS):  # 种群迭代进化GENERATIONS代

    Crossover_and_Mutation(population, CROSSOVER_RATE, i)  # 种群通过交叉变异产生后代
    fitness = get_fitness(population, i)[:]                # 对种群中的每个个体进行评估
    population = select(fitness)[:]                        # 选择生成新的种群

    print("进度:%d/%d  current_fitness = " % (i, GENERATIONS), max(fitness))
    if max(fitness) > best_fitness:
        best_fitness = max(fitness)
        best_one = dic[best_fitness]
    dic = {}
    dic[best_fitness] = best_one

x = dic[best_fitness]
print(x)
print("best_fitness:",best_fitness)
for i in range(N):#绘制散点图
    plt.scatter(x[2*i], x[2*i+1])
end = time.time()#结束计时

print('POP_SIZE = %s\nCROSSOVER_RATE = %s\nMUTATION_RATE = %s\nGENERATIONS = %s'
      %(POP_SIZE,CROSSOVER_RATE,MUTATION_RATE,GENERATIONS))
print('Running time: %s seconds'%(end-start))
plt.show()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值