• 遗传算法 - 函数最优解计算


    遗传算法

    遗传算法概念

    遗传算法的概念是在 1975 年由密切根大学的 J.Holland 提出的,这是一种通过模拟自然进化过程寻找最优解的方法。它遵循达尔文的物竞天择,适者生存的进化准则。基本思想:

    初始一个种群,选择种群中适应度高的个体进行交叉变异。然后再将适应度低的个体淘汰,留下适应度高的个体进行繁殖,这样不断的进化,最终留下的都是优秀的个体。

    基因和染色体

    在遗传算法中,我们首先需要将要解决的问题映射成一个数学问题,也就是所谓的数学建模,那么这个问题的一个可行解即被称为一条染色体或个体。如:

    3x+4y+5z<100

    [1,2,3],[2,3,4],[3,2,1]均为这个函数的可行解,这些可行解在遗传算法中均被称为染色体,每一个元素就被称为染色体上的一个基因。

    染色体编码与解码

    遗传算法的运算对象是表示染色体的符号串,所以必须把变量 x,y,z 编码为一种符号串。常见的编码方式如用无符号二进制整数来表示。解码即将二进制整数转换回最初的表现型。

    编码: 5 --> 0101。

    解码: 0101 --> 5。

    初始群体的产生

    遗传算法是对群体进行的进化操作,需要给其准备一些表示起始搜索点的初始群体数据。假如群体规模的大小取为 4,即群体由 4 个染色体组成,每个染色体可通过随机方法产生。

    如: 011101 , 101011 , 011100 , 111001。

    物竞天择

    适应度函数:遗传算法中以染色体适应度的大小来评定各个染色体的优劣程度,从而决定其遗传机会的大小。

    选择函数:自然界中,越适应的个体就越有可能繁殖后代。但是也不能说适应度越高的就肯定后代越多,只能是从概率上来说更多。常用的选择方法有轮盘赌选择法。

    f_i表示每个染色体的适应度,则每个个体遗传下来的概率为:

    p(i)=\frac{f_i}{\sum_i{f_i}}

    由公式可以看出,适应度越高,则遗传下来的概率就越大,好比赌轮盘,轮盘上所占面积越大,则被小球滚到的概率就越大。

    交叉与变异

    交叉是遗传算法中产生新的个体的主要操作过程,以一定的概率决定个体间是否进行交叉操作。

    如上图为父辈染色体进过交叉后产生新的染色体的过程。

    变异为另一种产生新个体的操作,它可以为种群带来多样性:

    也就是将我的染色体中的基因,随机的由0变成1,或1变成0。

    遗传算法流程

    1. 初始化种群
    2. 计算适应度
    3. 选择适应度高的个体
    4. 通过交叉变异选择新的染色体
    5. 终止进化

    使用python实现遗传算法。

    本关任务是使用遗传算法求解目标函数最大值,首先需要随机产生很多个解,即初始化种群,代码如下:

    1. #初始化种群
    2. pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))
    3. # Size函数需要传入两个参数 POP_SIZE为解的个数,即染色体个数。DNA_SIZE为染色体长度

    其中POP_SIZE为解的个数,即染色体个数。DNA_SIZE为染色体长度。

    由于染色体为二进制编码,所以还需要将二进制转换为浮点数的解码方法:

    1. #解码
    2. def translateDNA(pop):
    3. return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]

    上述代码中: pop.dot(2 ** np.arange(DNA_SIZE)[ : : -1])已经转换成十进制但是需要归一化到 0~5 ,如有 1111 这么长的 DNA,要产生的十进制数范围是 [0, 15] ,而所需范围是 [-1, 1] ,就将 [0,15] 缩放到 [-1,1] 这个范围。

    a[ : : -1]相当于a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍。所以你看到一个倒序,np.arange(DNA_SIZE)[ : : -1]得到 10,9,8,...,0 。

    如将 10101 转换到 0 到 5 之间:

    x=\frac{2^4+2^2+2+0}{2^5-1}\times 5

    然后计算每个染色体的适应度,由于是求解最大值,函数值越大则越应该被选择,所以,将每个染色体对应的函数值减去最小值作为适应度:

    1. #获取染色体适应度
    2. def get_fitness(pred):
    3. return pred + 1e-3 - np.min(pred)

    再选择适应度高的染色体:

    1. #选择适应度高的染色体
    2. def select(pop, fitness):
    3. idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
    4. p=fitness/fitness.sum())
    5. return pop[idx]

    再对染色体进行交叉变异操作:

    1. #交叉
    2. def crossover(parent, pop):
    3. if np.random.rand() < CROSS_RATE:
    4. i_ = np.random.randint(0, POP_SIZE, size=1)
    5. cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool)
    6. parent[cross_points] = pop[i_, cross_points]
    7. return parent
    8. #变异
    9. def mutate(child):
    10. for point in range(DNA_SIZE):
    11. if np.random.rand() < MUTATION_RATE:
    12. child[point] = 1 if child[point] == 0 else 0
    13. return child

    总流程如下:

    1. def F(x): return np.sin(10*x)*x + np.cos(2*x)*x
    2. def ga(F):
    3. '''
    4. F:需要求解的函数
    5. '''
    6. #初始化种群
    7. pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))
    8. #开始进化
    9. for _ in range(N_GENERATIONS):
    10. #计算函数值
    11. F_values = F(translateDNA(pop))
    12. #计算适应度
    13. fitness = get_fitness(F_values)
    14. #选择适应度高的个体
    15. pop = select(pop, fitness)
    16. pop_copy = pop.copy()
    17. #通过交叉变异选择新的染色体
    18. for parent in pop:
    19. #交叉产生子代
    20. child = crossover(parent, pop_copy)
    21. #变异产生子代
    22. child = mutate(child)
    23. #子代代替父代
    24. parent[:] = child
    25. #获取最优解
    26. x = translateDNA(pop)[-1]
    27. return x

    其中:

    1. N_GENERATIONS为进化轮数。
    2. CROSS_RATE为交叉概率。
    3. MUTATION_RATE 为变异概率。
    4. X_BOUND 为函数定义域。

    具体例子

    实现遗传算法。并求解函数f(x)在区间 [0,5] 上的最大值:

    f(x)=xsin(10x)+xcos(2x)

    函数图像如下:

    1. #encoding=utf8
    2. import numpy as np
    3. DNA_SIZE = 10
    4. POP_SIZE = 100
    5. CROSS_RATE = 0.8
    6. MUTATION_RATE = 0.003
    7. N_GENERATIONS = 500
    8. X_BOUND = [0, 5]
    9. #获取染色体适应度
    10. def get_fitness(pred):
    11. return pred + 1e-3 - np.min(pred)
    12. #解码
    13. def translateDNA(pop):
    14. return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]
    15. #选择适应度高的染色体
    16. def select(pop, fitness):
    17. idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
    18. p=fitness/fitness.sum())
    19. return pop[idx]
    20. #交叉
    21. def crossover(parent, pop):
    22. if np.random.rand() < CROSS_RATE:
    23. i_ = np.random.randint(0, POP_SIZE, size=1)
    24. cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool)
    25. parent[cross_points] = pop[i_, cross_points]
    26. return parent
    27. #变异
    28. def mutate(child):
    29. for point in range(DNA_SIZE):
    30. if np.random.rand() < MUTATION_RATE:
    31. child[point] = 1 if child[point] == 0 else 0
    32. return child
    33. def ga(F):
    34. '''
    35. F:需要求解的函数
    36. x:最优解
    37. '''
    38. #初始化种群
    39. # 初始化种群(随机生成二进制染色体)
    40. pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))
    41. #开始进化
    42. for _ in range(N_GENERATIONS):
    43. #计算函数值
    44. F_values = F(translateDNA(pop))
    45. #计算适应度
    46. fitness = get_fitness(F_values)
    47. #选择适应度高的个体
    48. pop = select(pop, fitness)
    49. pop_copy = pop.copy()
    50. #通过交叉变异选择新的染色体
    51. for parent in pop:
    52. #交叉产生子代
    53. child = crossover(parent, pop_copy)
    54. #变异产生子代
    55. child = mutate(child)
    56. #子代代替父代
    57. parent[:] = child
    58. #获取最优解
    59. x = translateDNA(pop)[-1]
    60. return x
  • 相关阅读:
    一刷完结撒花
    希望计算机专业同学都知道这些老师
    ES6 Promise链式调用解决异步回调
    VMware 与戴尔正式“分手”
    conda创建python虚拟环境
    无代码开发卡片视图入门教程
    DVWA之SQL注入
    信息化发展55
    本章目标: 将SSM项目及数据库完整的部署CentOS7
    蓝桥杯基础---切面条
  • 原文地址:https://blog.csdn.net/weixin_50917576/article/details/136881979