遗传算法--函数最值问题

利用遗传算法计算函数最值

在对人工智能的学习过程中,初次接触到遗传算法,在这里记录一下我的学习过程和一些思考。
以下是我学习和参考的资料,下文中涉及的问题、内容和求解方法均来源于以下资料:

关于利用遗传算法解决函数最值问题的相关博客
关于遗传算法原理的讲解

刚开始接触遗传算法时,我觉得很抽象,难以理解,但经过实际问题的演练后,逐渐明白了其中的一点点原理。特别是函数最值问题,可以形象地类比为种群在环境中的生存问题。在遗传算法中,确定适应度函数是非常关键的。针对函数最值问题,适应度函数可以简单地选择原函数,自变量则对应种群中的个体。在求最大值的问题中,函数值即对应个体的适应度,适应度越高的个体就越容易存活。每一轮的迭代过程中,适应度低的个体会被淘汰。同时,为了削减初始值对最终结果的影响,需设定个体突变的概率。简单来说,适应度对应函数值,适应度函数对应目标函数,个体染色体编码对应自变量的值,生存环境对应我们设计的筛选规则。下面开始解决一个小问题。

考虑以下函数:
在这里插入图片描述
在区间(0,9)求函数最大值。

以下是我学习参考的代码,在原代码(上文第一个链接)基础上做了一点点修改:

import numpy as np
import matplotlib.pyplot as plt

# 适应度函数,即目标函数
def fitness(x):
    return x + 10 * np.sin(5 * x) + 7 * np.cos(4 * x)

# 个体类
class indivdual:
    def __init__(self):    #初始化定义
        self.x = 0  # 染色体编码,自变量
        self.fitness = 0  # 适应度值,因变量

    def __eq__(self, other):  #赋值定义
        self.x = other.x
        self.fitness = other.fitness


# 初始化种群函数,N为种群内个体数,pop为种群
def initPopulation(pop, N):
    for i in range(N):
        ind = indivdual()
        ind.x = np.random.uniform(0, 9)
        ind.fitness = fitness(ind.x)   
        pop.append(ind)    #个体加入种群中

# 选择过程
def selection(N):
    # 种群中随机选择2个个体进行变异(这里没有用轮盘赌,直接用的随机选择)
    return np.random.choice(N, 2)

# 结合/交叉过程,采用算数交叉的方法(两个个体线性组合)
def crossover(parent1, parent2):
    # 染色体保留百分比
    save1,save2 = 0.9 , 0.1    
    child1, child2 = indivdual(), indivdual()
    child1.x = save1 * parent1.x + save2 * parent2.x    
    child2.x = save2 * parent1.x + save1 * parent2.x    
    child1.fitness = fitness(child1.x)
    child2.fitness = fitness(child2.x)
    return child1, child2


# 变异过程
def mutation(pop):
    # 种群中随机选择一个进行变异
    ind = np.random.choice(pop)
    # 用随机赋值的方式进行变异
    ind.x = np.random.uniform(0, 9)
    ind.fitness = fitness(ind.x)

# 最终执行
def implement():
    # 种群中个体数量
    N = 20
    # 种群
    POP = []
    #记录种群初始状态
    POP_init = []
    # 迭代次数
    iter_N =1800
    # 初始化种群
    initPopulation(POP, N)
    # 记录种群初始个体
    POP_init = POP.copy()
    #个体间交叉结合概率
    crosscover_pro = 0.75
    #个体变异概率
    mutation_pro = 0.1
# 进化过程
    for it in range(iter_N): 
        a, b = selection(N)
        #交叉结合
        if np.random.random() < crosscover_pro:  
            child1, child2 = crossover(POP[a], POP[b])
            #对交叉结合后的四个个体进行排序
            new = sorted([POP[a], POP[b], child1, child2], key=lambda ind: ind.fitness, reverse=True)
            #留下四个个体中适应度较高的两个
            POP[a], POP[b] = new[0], new[1]
        #变异
        if np.random.random() < mutation_pro:  
            mutation(POP)  

        POP.sort(key=lambda ind: ind.fitness, reverse=True)   #key指定某一项进行比较
    return POP,POP_init
pop,pop_init = implement()
# 绘图代码
def func(x):
    return x + 10 * np.sin(5 * x) + 7 * np.cos(4 * x)
x = np.linspace(0, 9, 10000)
y = func(x)
scatter_x = np.array([ind.x for ind in pop])
scatter_y = np.array([ind.fitness for ind in pop])
#初始个体
init_x = np.array([ind.x for ind in pop_init])
init_y = np.array([ind.fitness for ind in pop_init])
print(scatter_x)
print(scatter_y)
plt.plot(x, y)
plt.scatter(scatter_x, scatter_y, c='r')
plt.scatter(init_x, init_y, c='b')
plt.show()

下图为代码运行结果

图中的蓝色曲线代表目标函数,蓝色点代表种群中的初始个体,红色点代表计算后剩下的个体,两个数组的第一个值分别代表最大值处自变量的值和函数最大值。

在得到结果后,我开始尝试调整运算过程中的参数值,以此达到检验结果的目的。

(1)将个体变异概率调节为0(之前为0.1)
在这里插入图片描述
从图中可以看到,剩余的个体集中在函数的几个极大值点处,但并没有我们想要的最大值。这里可以考虑将函数极大值处当作山峰。如果不进行变异,个体很难逾越到旁边的山峰,因为即使是他们的后代也继承了其父辈的部分染色体编码(x的值)。因此在变异概率为0时,初始化时个体的染色体编码对后续运算起到决定性作用。既然变异概率为0时得到的结果不理想,如果设置为较大会怎样呢?

(2)将变异概率设置为0.5(之前为0.1)
在这里插入图片描述
从图中可以看出,相比于变异概率0.1,红色点更加分散,但仍能够得到我们想要的结果。

(3)降低交叉结合概率至0.3,保持变异概率为0.1(之前为0.75)
在这里插入图片描述
由图可见,下调交叉结合概率后,剩余个体的分布更加分散,甚至无法保证得到我们想要的结果。

从以上的尝试中,可以发现在遗传算法中,个体间交叉结合的概率、个体变异的概率均会对结果有较大影响。在设计算法前,应对不同问题合理分析,设置合理的参数值。完成代码后,可以多次修改这些参数值,通过对比检验结果的准确性。当然对于遗传算法,影响运算结果的因素有很多,这里只是列举两个最简单的,毕竟我也是刚刚接触。

  • 5
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值