用遗传算法求函数最大值(python)

用遗传算法求函数最大值(python)

step1.可视化函数(pyecharts)

step2.求函数的最大值(遗传算法)

用遗传算法求函数最大值(python)_第1张图片

1: 可视化函数

没有安装pyecharts的先打开命令行输入下列命令:

pip install pyecharts

把官方文档https://pyecharts.org里的demo拿来修改一下即可:
实际上需要我们编写的只有create_data()这个函数!
用遗传算法求函数最大值(python)_第2张图片

代码如下:

import numpy as np
import pyecharts.options as opts
from pyecharts.charts import Surface3D


# -----------------绘制图像---------------------
# 获取绘制图像的数据
def create_data():
    data = []
    for i in np.arange(-10, 10, 0.1):
        for j in np.arange(-10, 10, 0.1):
            x = i
            y = j
            z = 0.5 - (np.sin(np.sqrt(x**2+y**2))**2 - 0.5) / (1 + 0.001*(x**2 + y**2)**2)
            data.append([x, y, z])
    # 没有下行的代码会无法显示
    data = [[item[1], item[0], item[2]] for item in data]
    return data


# 使用pyecharts绘制图像
def draw():
    (
        # Surface3D(init_opts=opts.InitOpts(width="1600px", height="800px"))
        Surface3D()
        .add(
            series_name="f(x,y)",
            shading="color",
            data=list(create_data()),
            xaxis3d_opts=opts.Axis3DOpts(type_="value"),
            yaxis3d_opts=opts.Axis3DOpts(type_="value"),
            grid3d_opts=opts.Grid3DOpts(width=100, height=40, depth=100),
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="z=f(x,y)"),
            visualmap_opts=opts.VisualMapOpts(
                dimension=2,
                max_=1,
                min_=-1,
                range_color=[
                    "#313695",
                    "#4575b4",
                    "#74add1",
                    "#abd9e9",
                    "#e0f3f8",
                    "#ffffbf",
                    "#fee090",
                    "#fdae61",
                    "#f46d43",
                    "#d73027",
                    "#a50026",
                ],
            )
        )
        .render("res.html")
    )

2:求最大值

先举例说明编码、适应度、选择、交叉和变异的思想

(1)编码
举个例子: 
个体是x的取值,取值范围是-10到10,精度为小数点后2位
把区间化成20*10^2=2000个小区间,2^10<2000<2^11,所以需要用11位2进制数表示。

这时还要解决一个问题:把编码(11位2进制数)映射到[-10,10]这个区间!

举个例子: 
11111111110 => -10 + (11111111110/11111111111)*20
-10是区间左端点
 20是区间长度
 11111111111是最大的编码
(2)适应度

在遗传算法中,适应度函数也叫评价函数,是用来判断群体中的个体的优劣程度的指标,它是根据所求问题的目标函数来进行评估的。在这个求函数最大值的问题中,该二元函数就是适应度函数,该值越大说明该个体对应的取值越接近最优解。

(3)选择(用轮盘赌算法实现)

该算法的思想是:每个个体的选择概率和其适应度值成比例,但是通常情况下,我们都是根据“累计概率”来实现。
举个例子:

个体 适应度(函数值) 选择概率 累积概率
A 10 10/(10+20+30+40)=0.1 0.1
B 20 10/(10+20+30+40)=0.2 0.1+0.2=0.3
C 30 30/(10+20+30+40)=0.3 0.3+0.3=0.6
D 40 40/(10+20+30+40)=0.4 0.6+0.4=1
产生一个随机数:
p = random.random()  #0-1之间 

若p=0.4,由于0.3<0.4<0.6,这时就应该选择个体C

(4)交叉(在遗传算法中起核心作用)

交叉是指把两个父代个体的部分结构加以替换重组而生成新个体的操作。

这里介绍单点交叉
step1.随机选择交叉点的位置;
step2.产生掩码;
step3.与对应掩码&运算
step4.拼接

举个例子: (个体编码为24位)
1.loc=21(从右往左数)
2.mask1: 11100000 00000000 00000000
  mask2: 00011111 11111111 11111111
3.chrom1&mask1=>chrom高3位
  chrom2&mask2=>chrom低21位
4.chrom1高3位+chrom2低21位 => new_chrom1
  chrom2高3位+chrom1低21位 => new_chrom2
(5)变异

(Ⅰ)引入变异的目的有两个:(参考百度百科)
1.使遗传算法具有局部的随机搜索能力。当遗传算法通过交叉算子已接近最优解邻域时,利用变异算子的这种局部随机搜索能力可以加速向最优解收敛。此种情况下的变异概率应取较小值,否则接近最优解的积木块会因变异而遭到破坏。
2.使遗传算法可维持群体多样性,以防止出现未成熟收敛现象。此时收敛概率应取较大值。
(Ⅱ)实现步骤:
step1: 随机选择变异位置;
step2: 产生掩码1
step3: 编码与掩码‘&’运算(;这是0->1突变和1->0突变的依据)
若结果大于0,说明突变处是1,否则是0;
step4: 若突变处是1:编码与掩码2进行与运算;
若突变处是0:编码与掩码2进行或运算。

举个例子: (个体编码为24位)
  1. loc=24(从右往左数)
  2. mask1: 10000000 00000000 00000000
  3. res=10000000 00000000 00000000 或 00000000 00000000 00000000
  4. 若res=10000000 00000000 00000000 => mask2 = 01111111 11111111 11111111
     则个体编码与mask2进行'&'运算;
     若res=00000000 00000000 00000000 => mask2 = 10000000 00000000 00000000
     则个体编码与mask2进行'|'运算;

完整代码如下:(来源:知乎上的一篇文章)

import math, random

class Population:
    # 种群的设计
    def __init__(self, size, chrom_size, cp, mp, gen_max):
        # 种群信息合
        self.individuals = []          # 个体集合
        self.fitness = []              # 个体适应度集
        self.selector_probability = [] # 个体选择概率集合
        self.new_individuals = []      # 新一代个体集合

        self.elitist = {'chromosome':[0, 0], 'fitness':0, 'age':0} # 最佳个体的信息

        self.size = size # 种群所包含的个体数
        self.chromosome_size = chrom_size # 个体的染色体长度
        self.crossover_probability = cp   # 个体之间的交叉概率
        self.mutation_probability = mp    # 个体之间的变异概率
         
        self.generation_max = gen_max # 种群进化的最大世代数
        self.age = 0                  # 种群当前所处世代
          
        # 随机产生初始个体集,并将新一代个体、适应度、选择概率等集合以 0 值进行初始化
        v = 2 ** self.chromosome_size - 1
        for i in range(self.size):
            self.individuals.append([random.randint(0, v), random.randint(0, v)])
            self.new_individuals.append([0, 0])
            self.fitness.append(0)
            self.selector_probability.append(0)

    # 基于轮盘赌博机的选择
    def decode(self, interval, chromosome):
        '''将一个染色体 chromosome 映射为区间 interval 之内的数值'''
        d = interval[1] - interval[0]
        n = float (2 ** self.chromosome_size -1)
        return (interval[0] + chromosome * d / n)
     
    def fitness_func(self, chrom1, chrom2):
        '''适应度函数,可以根据个体的两个染色体计算出该个体的适应度'''
        interval = [-10.0, 10.0]
        (x, y) = (self.decode(interval, chrom1), 
                  self.decode(interval, chrom2))
        n = lambda x, y: math.sin(math.sqrt(x*x + y*y)) ** 2 - 0.5
        d = lambda x, y: (1 + 0.001 * (x*x + y*y)) ** 2
        func = lambda x, y: 0.5 - n(x, y)/d(x, y)
        return func(x, y)
         
    def evaluate(self):
        '''用于评估种群中的个体集合 self.individuals 中各个个体的适应度'''
        sp = self.selector_probability
        for i in range (self.size):
            self.fitness[i] = self.fitness_func (self.individuals[i][0],   # 将计算结果保存在 self.fitness 列表中
                                                 self.individuals[i][1])
        ft_sum = sum (self.fitness)
        for i in range (self.size):
            sp[i] = self.fitness[i] / float (ft_sum)   # 得到各个个体的生存概率
        for i in range (1, self.size):
            sp[i] = sp[i] + sp[i-1]   # 需要将个体的生存概率进行叠加,从而计算出各个个体的选择概率

    # 轮盘赌博机(选择)
    def select(self):
        (t, i) = (random.random(), 0)
        for p in self.selector_probability:
            if p > t:
                break
            i = i + 1
        return i

    # 交叉
    def cross(self, chrom1, chrom2):
        p = random.random()    # 随机概率
        n = 2 ** self.chromosome_size -1
        if chrom1 != chrom2 and p < self.crossover_probability:
            t = random.randint(1, self.chromosome_size - 1)   # 随机选择一点(单点交叉)
            mask = n << t    # << 左移运算符
            (l1, l2) = (chrom1 & mask, chrom2 & mask)   # & 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
            mask = n >> (self.chromosome_size - t)
            (r1, r2) = (chrom1 & mask, chrom2 & mask)
            (chrom1, chrom2) = (l1 + r2, l2 + r1)
        return (chrom1, chrom2)

    # 变异
    def mutate(self, chrom):
        p = random.random ()
        if p < self.mutation_probability:
            t = random.randint (1, self.chromosome_size)
            mask1 = 1 << (t - 1)
            mask2 = chrom & mask1
            if mask2 > 0:
                chrom = chrom & (~mask2)  # ~ 按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1 
            else:
                chrom = chrom | mask1   # ^ 按或运算
        return chrom

    # 保留最佳个体
    def reproduct_elitist (self):
        # 与当前种群进行适应度比较,更新最佳个体
        j = -1
        for i in range (self.size):
            if self.elitist['fitness'] < self.fitness[i]:
                j = i
                self.elitist['fitness'] = self.fitness[i]
        if (j >= 0):
            self.elitist['chromosome'][0] = self.individuals[j][0]
            self.elitist['chromosome'][1] = self.individuals[j][1]
            self.elitist['age'] = self.age

    # 进化过程
    def evolve(self):
        indvs = self.individuals
        new_indvs = self.new_individuals
        # 计算适应度及选择概率
        self.evaluate()
        # 进化操作
        i = 0
        while True:
            # 选择两个个体,进行交叉与变异,产生新的种群
            idv1 = self.select()
            idv2 = self.select()
            # 交叉
            (idv1_x, idv1_y) = (indvs[idv1][0], indvs[idv1][1])
            (idv2_x, idv2_y) = (indvs[idv2][0], indvs[idv2][1])
            (idv1_x, idv2_x) = self.cross(idv1_x, idv2_x)
            (idv1_y, idv2_y) = self.cross(idv1_y, idv2_y)
            # 变异
            (idv1_x, idv1_y) = (self.mutate(idv1_x), self.mutate(idv1_y))
            (idv2_x, idv2_y) = (self.mutate(idv2_x), self.mutate(idv2_y))
            (new_indvs[i][0], new_indvs[i][1]) = (idv1_x, idv1_y)  # 将计算结果保存于新的个体集合self.new_individuals中
            (new_indvs[i+1][0], new_indvs[i+1][1]) = (idv2_x, idv2_y)
            # 判断进化过程是否结束
            i = i + 2         # 循环self.size/2次,每次从self.individuals 中选出2个
            if i >= self.size:
                break
        
        # 最佳个体保留
        # 如果在选择之前保留当前最佳个体,最终能收敛到全局最优解。
        self.reproduct_elitist()

        # 更新换代:用种群进化生成的新个体集合 self.new_individuals 替换当前个体集合
        for i in range (self.size):
            self.individuals[i][0] = self.new_individuals[i][0]
            self.individuals[i][1] = self.new_individuals[i][1]

    def run(self):
        '''根据种群最大进化世代数设定了一个循环。
        在循环过程中,调用 evolve 函数进行种群进化计算,并输出种群的每一代的个体适应度最大值、平均值和最小值。'''
        for i in range (self.generation_max):
            self.evolve ()
            print (i, max (self.fitness), sum (self.fitness)/self.size, min (self.fitness))
if __name__ == '__main__':
    # 种群的个体数量为 50,染色体长度为 25,交叉概率为 0.8,变异概率为 0.1,进化最大世代数为 150
    pop = Population (50, 25, 0.8, 0.1, 150)
    pop.run()

你可能感兴趣的:(python,数据分析,人工智能)