ᕕ( ᐛ )ᕗ DawnStar's Blog

智能算法01 遗传算法

一、生物进化理论和遗传学基本知识

二、遗传算法概述

三、遗传算法中的基本概念

  1. 个体:模拟生物个体,即可行解,对应染色体。
  2. 种群(population):模拟生物种群,由若干个体组成,即可行解集
  3. 染色体(chromosome):可行解的编码表示二进制编码)。比如:个体9对应染色体1001。可行解编码的分量,成为基因(gene),01。当然,也可以通过实数编码
  4. 适应度(fitness):生物个体对环境的适应能力。
  5. 适应度函数(fitness function):遗传算法用来评价个体(解)优劣的数学函数。通常情况下,适应度函数值越大,解的质量越好。

四、遗传操作

1 选择

遗传算法使用选择运算实现对个体进行优胜劣汰操作:

  1. 适应度高的个体被遗传到下一代群体中的概率大
  2. 不产生新个体

经典的选择算子采用轮盘赌选择方法。 轮盘赌选择的基本思想是:个体被选中的概率与其适应度函数值大小成正比。设种群大小为 𝑛 ,个体 𝑖 的适应度为 𝐹𝑖 ,则个体 𝑖 被选中遗传到下一代群体的概率为: $$ 𝑃_𝑖=\frac{F_i}{\displaystyle \sum^{n}_{i = 1}F_i} $$ 例:假设有以下种群[16, 2, 8, 4, 1],设个体值即为适应度值,可得选择概率累积概率。轮盘赌选择可表示为:随机生成一个0~1的数,该数从上往下第一个小于的累积概率对应的个体,即为选择的个体。

image-20240603210420587

个体适应度选择概率累计概率
16160.5160.516
220.0650.581
880.2580.893
440.1290.968
110.0321
import numpy as np

s = [16, 2, 8, 4, 1]                        # 5个个体
s_p = list(map(lambda x: x / sum(s), s))    # 选择概率
r = np.random.random()                      # 0-1之间的随机数
m = 0
c = 0
for i in range(len(s)):
    m += s_p[i]                             # 求累积适应度
    if r <= m:
        c = i
        break
print(s[c])

也可以有其他选择的方式,如随机选择

def selection(pop):
    sum_fitness = 0                            # 适应度总和
    for i in range(len(pop)):
        sum_fitness += pop[i].fitness
    pop_new = [pop[0]]                         # 假定pop[0]是最优个体,保留
    while len(pop_new) < len(pop):             # 基于轮盘赌的复制操作,直到复制到原始种群规模
        r = np.random.random()
        m = 0
        for i in range(len(pop)):
            m += pop[i].fitness / sum_fitness
            if r <= m:
                pop_new.append(pop[i])
                break
    return pop_new

2 交叉

互换两个染色体某些位上的基因,是产生新个体的主要方法。

例1:设染色体S1=01001011,S2=10010101,交换其后4位基因,得S1’=01000101,S2’=10011011

例2:设个体1值为10,个体2值为20,按照2:8交叉,交叉后个体个体1′=10∗0.2+20∗0.8=18 , 个体个体2′=10∗0.8+20∗0.2=12

可以发生在一个断点或者多个断点。

img

def crossover(pop):
    for i in range(1, len(pop), 2):                                    # 0处最优个体保留
        p = np.random.random()
        if p < Pc and len(pop) > i + 1:                                # 两两交叉
            cross_range = np.random.random()
            pop_i = pop[i].x
            pop_j = pop[i + 1].x
            pop[i].x = pop_i * cross_range + pop_j * (1 - cross_range) 
            if fitness(pop[i].x) < fitness(pop_i):                     # 保留子代和父代中的最优个体
                pop[i].x = pop_i
            pop[i + 1].x = pop_j * cross_range + pop_i * (1 - cross_range)
            if fitness(pop[i + 1].x) < fitness(pop_j):
                pop[i + 1].x = pop_j
            pop[i].fitness = fitness(pop[i].x)
            pop[i + 1].fitness = fitness(pop[i + 1].x)
    return pop

3 变异

改变染色体某个(些)位上的基因。

例1:染色体S=11001101,第三位发生变异,得S’=11101101

例2:实数编码,个体值为10∈[0, 100],发送变异,即再随机生成一个数,如59。

def mutation(pop):
    pop_new = [pop[0]]                                    # 0处最优个体保留
    for i in range(1, len(pop)):
        p = np.random.random()
        ind = Individual()
        if p < Pm:                                        # 0-1随机数 小于 变异概率,则进行变异
            ind.x = Xx + (Xs - Xx) * np.random.random()   # 重新生成定义域内的一个随机数
            ind.fitness = fitness(ind.x)
            pop_new.append(ind)
        else:
            ind.x = pop[i].x                              # 大于等于 变异概率,不改
            ind.fitness = pop[i].fitness
            pop_new.append(ind)
    return pop_new

五、遗传算法步骤

  1. 初始化个体与种群;
  2. 对优化问题的解进行编码——实数编码/二进制编码;
  3. 个体评价,适应度函数的构造与应用;
  4. 遗传操作:选择、交叉、变异;
  5. 终止条件判断,不满足终止条件则转3。

img

六、Python实现

求解f = x +10* sin(5* x)+7* cos(4* x)在[0, 10]上的最大值。

import numpy as np
from matplotlib import pyplot as plt

NP = 50     # 种群数量
Pc = 0.8    # 交叉率
Pm = 0.1    # 变异率
G = 20      # 最大遗传代数
Xs = 10     # 上限
Xx = 0      # 下限


def fitness(x):
    return x + 10 * np.sin(5 * x) + 7 * np.cos(4 * x)


# 个体类
class Individual:
    def __init__(self):
        self.x = 0  # 染色体编码
        self.fitness = 0  # 个体适应度值

    def __eq__(self, other):
        self.x = other.x
        self.fitness = other.fitness


# 初始化种群
def init_population(pop, N):
    for i in range(N):
        individual = Individual()  # 个体初始化
        individual.x = np.random.uniform(Xx, Xs)  # 个体编码,0~10正态分布
        individual.fitness = fitness(individual.x)  # 计算个体适应度
        pop.append(individual)


# 选择:基于轮盘赌的复制操作
def selection(pop):
    sum_fitness = 0
    for i in range(len(pop)):
        sum_fitness += pop[i].fitness
    pop_new = [pop[0]]
    while len(pop_new) < len(pop):
        r = np.random.random()
        m = 0
        for i in range(len(pop)):
            m += pop[i].fitness / sum_fitness
            if r <= m:
                pop_new.append(pop[i])
                break
    return pop_new


# 交叉
def crossover(pop):
    for i in range(1, len(pop), 2):
        p = np.random.random()
        if p < Pc and len(pop) > i + 1:
            cross_range = np.random.random()
            pop_i = pop[i].x
            pop_j = pop[i + 1].x
            pop[i].x = pop_i * cross_range + pop_j * (1 - cross_range)
            if fitness(pop[i].x) < fitness(pop_i):
                pop[i].x = pop_i
            pop[i + 1].x = pop_j * cross_range + pop_i * (1 - cross_range)
            if fitness(pop[i + 1].x) < fitness(pop_j):
                pop[i + 1].x = pop_j
            pop[i].fitness = fitness(pop[i].x)
            pop[i + 1].fitness = fitness(pop[i + 1].x)
    return pop


# 变异
def mutation(pop):
    pop_new = [pop[0]]
    for i in range(1, len(pop)):
        p = np.random.random()
        ind = Individual()
        if p < Pm:
            ind.x = Xx + (Xs - Xx) * np.random.random()
            ind.fitness = fitness(ind.x)
            pop_new.append(ind)
        else:
            ind.x = pop[i].x
            ind.fitness = pop[i].fitness
            pop_new.append(ind)
    return pop_new

# 遗传算法
def gene_algorithm():
    pop = []
    init_population(pop, NP)
    best = sorted(pop, key=lambda pop: pop.fitness, reverse=True)[0]  # 最佳点
    pop[0] = best
    for k in range(G):
        pop = selection(pop)
        pop = crossover(pop)
        pop = mutation(pop)
        best = sorted(pop, key=lambda pop: pop.fitness, reverse=True)[0]  # 最佳点
        pop[0] = best
    print(best.fitness)
    return pop


if __name__ == "__main__":
    pop = gene_algorithm()
    # 绘图
    x = np.linspace(Xx, Xs, 100000)
    y = fitness(x)
    scatter_x = np.array([ind.x for ind in pop])
    print(scatter_x)
    scatter_y = np.array([ind.fitness for ind in pop])
    best = sorted(pop, key=lambda pop: pop.fitness, reverse=True)[0]
    print('best_x', best.x)
    print('best_y', best.fitness)
    plt.plot(x, y)
    plt.scatter(scatter_x, scatter_y, c='r')
    plt.scatter(best.x, best.fitness, c='g', label='best point')
    plt.legend()
    plt.show()

img

#智能算法