遗传算法求取函数最值问题

news2024/12/28 5:45:24

目录

1. 关于遗传算法

2. 遗传算法的步骤

3. 代码实现

3.1 工具函数

3.1.1 目标函数

3.1.2 解码

3.1.3 交叉

3.1.4 变异

3.2 主函数部分

3.3 代码

4. 其他


1. 关于遗传算法

遗传算法是根据生物进化论提出的计算最优解的一种算法,核心思想是物竞天择,适者生存

网上关于遗传算法的讲解很多,本章会利用python实现遗传算法实现计算函数最大值,将其中的细节进行讨论

本章利用的函数为:y = x^2 ,定义域为0-10

关于遗传算法利用的其实是基因的遗传重组,所以这里每一个基因都是二进制的序列,例如10101

那二进制映射回定义域就是解码过程, start + (end - start) * tmp / (pow(2,length)-1) ,start 是定义域的左值,end 是定义域的右值,tmp是二进制序列,length 是二进制序列的长度。通过这样的计算就可以将二进制映射回给定的定义域中

那么二进制序列的长度如何定义?

是根据计算精度而言的,例如这里要计算的精度为0.01。那么就需要0.01->0.02->...->10.00中间共有10*10^2 = 1000个数字,那么需要的二进制就是10,因为2^10 = 1024可以保存1000个数字。那么二进制的序列长度就是 10

2. 遗传算法的步骤

接下来看看遗传算法的步骤:

这里只是根据自己实现的代码进行步骤分析,有些细节或者和网上实现不同的地方会在最后补充

1. 初始化种群 : 因为产生的二进制序列一般来说不是一个,这样是为了下面更好的交叉操作。并且,多个二进制序列也有利于找到最优的解

2. 解码 : 解码的意思就是将产生的二进制序列映射到对应的定义域当中,因为二进制产生的值很大,例如上述例子,定义域在0-10,精度为0.01,那么二进制长度为10,如果不进行映射的话,随便的一个二进制都会超过定义域10

当解码完成后,初始化种群已经全部变成在定义域当中的随机n(产生二进制序列的个数)个点

3. 计算适应度 :将定义域中的随机解码的点进行计算,例子中的y = x^2 被称为适应度函数

计算适应度其实就是计算这些值(x)对应的y,比如要求取最大值,那么看一下随机产生的x,哪一个y大,然后对较大的进行操作。这样反复操作的话,就可以找到最大值

4. 轮盘赌选取父系存活概率 : 当随机产生的n个点,计算完适应度函数后,会生成对应的n个函数值。函数值越大,我们说他越好(我们要计算最大值),那么他应该存活的概率就大。对应的方法是轮盘赌,例如产生的y值: 2 和 9,那么2/(2+9) 和 9/(2+9) 就是2和9存活的概率,这种就是轮盘赌。

注意:这只是概率,不是绝对的 9 > 2 ,就一定是 9 存活 

5. 交叉 : 交叉就是根据父系的二进制,将随机的比特位进行交换的操作,这样遗传的思维就出现了。

6. 变异 : 变异是为了跳出极值,将子代的二进制编码,随机取反。0- >1,1->0

具体的实现方式在代码里面讲解

3. 代码实现

如图,本章遗传算法实现的是计算y = x^2 的最大值

关于遗传算法的定义在这:

 

其中,mutation_rate 是二进制序列变异的概率,这个不应该过大,要不然子代就完全和父代不一样,那么遗传算法也就失去了意义

parents_rate 是父代中保存的概率,例如这里总共有10个种群,0.3就会保存3个父代,这里保存的方式是通过轮盘赌实现

实现的效果为:

 

3.1 工具函数

为了代码的模块化,这里utils里面存放了四个函数

3.1.1 目标函数

也就是适应度函数

# 目标函数
def function(x):
    # y = np.sin(x) * np.exp(-x)
    y = x**2
    return y

3.1.2 解码

decode 是根据传递的二进制序列矩阵bit_matrix(n*m,n是种群个数,m是二进制的长度) 进行编码,产生n个在start-end定义域中的十进制自变量

# 将二进制编码为十进制,并映射到定义域中
def decode(bit_matrix,num_group,start,end,length):
    ret = np.zeros(num_group)
    temp = []       # 保存转换的十进制数
    for i in range(num_group):
        tmp = int(''.join(map(lambda x:str(x),bit_matrix[i])),2)    # 获得每一条染色体的十进制
        ret[i] = start + (end - start) * tmp / (pow(2,length)-1)        # 映射回原始的定义域
        temp.append(tmp)
    return temp,ret

3.1.3 交叉

这里实现的方法有所不同

本章实现的遗传算法,种群数目是固定的 ,也就是说初始化是10个,那么父代保留了3个后,交叉产生的子代就只有7个,也就是代码中的count变量。

parents_groups 是父代的所有种群,而非保留之后的3个

实现交叉的方式为,将两组随机的二进制序列进行交叉。最后返回

# 交叉繁殖
def cross(count,parents_groups,length,cross_num=2):
    childen = []                        # 子代

    while len(childen) != count:       # 保证子代的数量和父代一样
        index = np.random.choice(np.arange(length),cross_num,replace=False)   # 随机交换cross_num个基因
        male = parents_groups[np.random.randint(0,len(parents_groups+1))]       # 从父代中随机挑选两个交叉繁殖
        female = parents_groups[np.random.randint(0,len(parents_groups+1))]

        childen_one = male.copy()
        childen_two = female.copy()

        childen_one[index] = female[index]          # 交换父母双方的基因产生两个子代
        childen.append(childen_one)

        if len(childen) == count:
            break

        childen_two[index] = male[index]
        childen.append(childen_two)
    return np.array(childen)

3.1.4 变异

变异是为了种群能够产生突变,这样随机产生的新的子代也许能够跳出极值

实现方式也很简单,num_mutation可以控制二进制变异的个数

# 变异
def mutation(children,mutation_rate,length,num_mutation=1):
    children_mutation = []
    for i in range(len(children)):
        tmp = children[i]
        if np.random.random() < mutation_rate:
            index = np.random.choice(np.arange(length),num_mutation,replace=False)

            for j in range(num_mutation):       # 变异
                if tmp[index[j]] == 1:
                    tmp[index[j]] = 0
                else:
                    tmp[index[j]]= 1
        children_mutation.append(tmp)

    return np.array(children_mutation)

3.2 主函数部分

有几点需要注意,计算适应度的时候,将它进行下面的操作,要不然轮盘赌选择的时候会报错。因为概率不能为负值

 

3.3 代码

主函数部分:

import numpy as np
import matplotlib.pyplot as plt
from utils import decode,function,cross,mutation


# 设定超参数
start,end = 0,10
length = 10                     # 染色体长度 bit,精度
num_group = 10                  # 种群数量
iteration_time = 2000             # 迭代次数
mutation_rate = 0.1             # 变异率
parents_rate = 0.3              # 父代中的保存个数(概率)


# 初始化二进制种群
init_group = np.random.randint(0,2,size=(num_group,length))

parents_group = init_group      # 父代

# 迭代
decode_parents_group = 0
for i in range(iteration_time):

    # 将二进制种群转为十进制,并映射到定义域中
    _, decode_parents_group = decode(bit_matrix=parents_group, num_group=num_group, start=start, end=end, length=length)

    # 计算种群适应度
    f = function(decode_parents_group)
    f = (f - np.min(f))+1e-8     # 防止 f 为负值或 0

    select = np.random.choice(np.arange(num_group),int(num_group*parents_rate),replace=True,p=f/sum(f))
    best_parents_group = parents_group[select]       # 父代中的保留

    count = len(parents_group) - len(best_parents_group)     # 计算差值

    # 交叉繁殖
    children = cross(count=count, parents_groups=parents_group, length=length)
    children = np.concatenate((best_parents_group, children))
    # 变异
    children = mutation(children=children,mutation_rate=mutation_rate,length=length)

    parents_group = children

fun = function(decode_parents_group)
x = np.linspace(start,end,100)
plt.plot(x,function(x),color='r')
plt.scatter(decode_parents_group,function(decode_parents_group))
plt.title('max is :%.4f' % np.max(fun))
plt.show()

utils 部分:

import numpy as np


# 目标函数
def function(x):
    # y = np.sin(x) * np.exp(-x)
    y = x**2
    return y


# 将二进制编码为十进制,并映射到定义域中
def decode(bit_matrix,num_group,start,end,length):
    ret = np.zeros(num_group)
    temp = []       # 保存转换的十进制数
    for i in range(num_group):
        tmp = int(''.join(map(lambda x:str(x),bit_matrix[i])),2)    # 获得每一条染色体的十进制
        ret[i] = start + (end - start) * tmp / (pow(2,length)-1)        # 映射回原始的定义域
        temp.append(tmp)
    return temp,ret


# 交叉繁殖
def cross(count,parents_groups,length,cross_num=2):
    childen = []                        # 子代

    while len(childen) != count:       # 保证子代的数量和父代一样
        index = np.random.choice(np.arange(length),cross_num,replace=False)   # 随机交换cross_num个基因
        male = parents_groups[np.random.randint(0,len(parents_groups+1))]       # 从父代中随机挑选两个交叉繁殖
        female = parents_groups[np.random.randint(0,len(parents_groups+1))]

        childen_one = male.copy()
        childen_two = female.copy()

        childen_one[index] = female[index]          # 交换父母双方的基因产生两个子代
        childen.append(childen_one)

        if len(childen) == count:
            break

        childen_two[index] = male[index]
        childen.append(childen_two)
    return np.array(childen)


# 变异
def mutation(children,mutation_rate,length,num_mutation=1):
    children_mutation = []
    for i in range(len(children)):
        tmp = children[i]
        if np.random.random() < mutation_rate:
            index = np.random.choice(np.arange(length),num_mutation,replace=False)

            for j in range(num_mutation):       # 变异
                if tmp[index[j]] == 1:
                    tmp[index[j]] = 0
                else:
                    tmp[index[j]]= 1
        children_mutation.append(tmp)

    return np.array(children_mutation)

4. 其他

这里用很多地方和网上实现的不一致,还有一些地方自己也不是特别明白

例如,保留父代的时候,可以重复保留吗?

本章的方法是可以(改为False就可以不重复),这里个人认为,如果选择保留父代不重复的话,那么基本上保留的父代就是按照概率值从大到小进行保留,那么初始化不太好的时候,很容易掉入极值的坑里

    select = np.random.choice(np.arange(num_group),int(num_group*parents_rate),replace=True,p=f/sum(f))

其他的例如,如果变异率或者变异的个数过多的话,那么父代留给子代的信息就完全被破坏了,那么遗传的意义也就没有了

这里计算 y = np.sin(x) * np.exp(-x) 的结果为:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/455834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

九龙证券|光模块概念股封单资金超3亿元,传媒板块涨停潮来袭

今天A股三大股指低开低走。沪深两市收盘共37股涨停。剔除4只ST股&#xff0c;合计33股涨停。另外&#xff0c;10股封板未遂&#xff0c;整体封板率为78.72%。 涨停战场&#xff1a; 华工科技封单资金超3亿元 从收盘涨停板封单量来看&#xff0c;同方股份封单量最高&#xff0…

【UE】制作简单的山脉地形

在上一篇博客中&#xff08;【UE】使用Quixel Bridge下载免费贴图&#xff09;&#xff0c;介绍了如何下载免费贴图&#xff0c;本篇博客介绍如何使用这些贴图制作地形贴图。 1. 创建地形 2. 用雕刻工具绘制地形 3. 新建两个材质函数&#xff0c;分别命名为“GrassAuto”、“R…

UWERANSIM - OAI5GC分立部署教程

环境&#xff1a; Ubantu18.04OAI-5GCv1.5.0UERANSIMv3.2.6 网络&#xff1a; Host1&#xff1a;OAI-5GCens37&#xff1a;192.168.12.3Host2&#xff1a;UERANSIMens40&#xff1a;192.168.12.33 确保两台宿主机之间互通&#xff01; 网络配置 Host1 网络&#xff1a;OA…

掌握 Web3 游戏数据分析,详述 4 个开发者需追踪的关键指标

引入&#xff1a;需要关注的关键指标包括哪些 区块链游戏在开发运营过程中需要追踪的关键指标包括红馆加密市场数据&#xff0c;DAU、MAU 和用户留存相关的用户数据、社交媒体参与数据&#xff0c;以及游戏内资产等生态系统相关数据。 主要观点&#xff1a; GameFi 项目与传统…

我国集成电路行业发展重心逐步转向芯片设计 高端设备材料领域国产化迫在眉睫

1、集成电路概念及其产业链图解 集成电路&#xff08;integrated circuit&#xff09;缩写为IC&#xff0c;是一种微型电子器件或部件&#xff0c;其是采用一定工艺将一块电路所需的晶体管、二极管、电阻、电容和电感等电子元器件制做在一块或几小块晶片或晶片上&#xff0c;然…

Navicat连接oracle数据库时报ORA-28547错的解决方法(亲测有效)

​ 文章目录 一. 问题说明二. 问题解决1. 查询Oracl版本2. 下载Oracle对应oci.dll文件3. 修改oci配置4. 重启Navicat并连接Oracle5. 总结navicat配置oci教程步骤 一. 问题说明 这是因为Navicat自带的oci.dll并不支持oracle11g&#xff0c;需要去官网下载对应支持的版本。 二.…

WIFI6模块AP6275系列选性参考和外围应用电路参考

AP6275系列是采用28纳米工艺的BCM43752方案设计、封装15x13mm/LGA-50&#xff0c;有SDIO接口和PCIe接口可选&#xff0c;同时还有蓝牙和WiFi天线是否共用区分&#xff1b;具体如下&#xff1a; AP6275S通信接口SDIO3.0WiFi/UARTBT5.3&#xff1b;吞吐量TX>450Mbps、RX>55…

Raft 共识算法1-Raft基础

Raft 共识算法1-Raft基础 Raft算法中译版地址&#xff1a;http://www.redisant.cn/etcd/contact 英原论文地址&#xff1a;https://raft.github.io/raft.pdf Etcd Assistant 是一款 etcd 可视化管理软件&#xff0c;便捷高效地操作您的 etcd 集群&#xff1b;支持多种键的视图&…

【Cisco Packet Tracer| 三.单交换机划分VLAN】

文章目录 一.实验原理二.连接实验拓扑图1.给主机和交换机之间连线2.给四台主机设置IP地址 三.未划分VLAN情况下查看4台主机间是否能ping通四.创建并划分VLAN1.创建VLAN2.划分VLAN 五.划分VLAN后查看4台主机间是否能ping通 一.实验原理 原理&#xff1a;单交换机连接4台主机&…

医药之家:国内首个三价轮状病毒疫苗上市!预防轮状病毒导致的婴幼儿腹泻

医药之家获悉&#xff0c;4月17日&#xff0c;国药集团中国生物兰州生物制品研究所自主研发的口服三价轮状病毒减毒活疫苗&#xff08;Vero细胞&#xff09;获批上市&#xff0c;这也是国内首个获准上市的三价轮状病毒疫苗。 轮状病毒是一种双链RNA病毒&#xff0c;常见于6个月…

若依系统部署在linux系统 验证码报错:FontConfiguration.getVersion报空指针异常

最近遇到一个问题&#xff1a;若依前后端分离后端服务部署到linnux系统后访问/capthaImage 报空指针异常&#xff01; 报错如下&#xff1a; 首先看问题sun.awt.FontConfiguration.getVersion(); 这是jdk的问题啊&#xff01; 首先查看linux系统的jdk信息 openjdk version &q…

数据库基础篇 《6. 多表查询》

目录 1. 一个案例引发的多表连接 1.1 案例说明 1.2 笛卡尔积&#xff08;或交叉连接&#xff09;的理解 1.3 案例分析与问题解决 2. 多表查询分类讲解 分类1&#xff1a;等值连接 vs 非等值连接 等值连接 非等值连接 ​编辑 分类2&#xff1a;自连接 vs 非自连接 分类3&…

C语言从入门到精通第7天(scanf、printf、getchar函数的使用)

scanf、printf、getchar函数的使用 printf函数scanf函数getchar函数 printf函数 在C语言中使用printf函数进行格式化的输出&#xff0c;它是一个可变的参数函数&#xff0c;参数的个数不定。在前面我们已经学习了各个类型的打印格式&#xff0c;这里就对最常见的输出格式进行介…

如何在模拟器里面脱360的壳

1.准备环境 1.夜神模拟器 2.pyhton3.8 3.frida的版本 16.0.17 4.frida-dexdump 2.设置adb连接 我们打开夜神模拟器所在的文件夹&#xff0c;里面有自带的adb&#xff0c;我们在这个文件夹里面打开cmd。在里面链接上夜神模拟器。 adb devices 我这边显示链接成功了。 3.…

全平台数据(数据库)管理工具 DataCap 管理 Rainbond 上的所有数据库

DataCap是用于数据转换、集成和可视化的集成软件&#xff0c;支持多种数据源、文件类型、大数据相关数据库、关系数据库、NoSQL数据库等。通过该 DataCap 可以实现对多个数据源的管理&#xff0c;对数据源下的数据进行各种操作转换&#xff0c;制作数据图表&#xff0c;监控数据…

蓝牙耳机哪个牌子的音质好一些?三百内音质最好的蓝牙耳机排行

近几年&#xff0c;随着蓝牙耳机的普及&#xff0c;越来越多的人喜欢在追剧、运动以及听歌时戴蓝牙耳机。蓝牙耳机的性能越来越强大&#xff0c;使用也越来越方便。下面&#xff0c;我来给大家推荐几款三百内音质好的蓝牙耳机&#xff0c;可以当个参考。 一、南卡小音舱Lite2蓝…

Ext4日志优化-iJournaling

背景 这几年随着SSD等高性能介质的普及&#xff0c;及其在大规模分布式存储系统上的应用。基于Append only的日志写入技术也应用得越来越多&#xff0c;这几天刚好有空&#xff0c;重读了Ext4文件系统的日志部分的内容&#xff0c;也正好看到一篇对Ext4日志技术进行优化的论文…

Measuement Comuputing 公司USB-1608GX设备EPICS驱动的安装和调试记录

1、USB-1608GX模块简介 单端输入的管脚图 差分输入管脚图 USB-1608GX模块具有以下功能&#xff1a; 1&#xff09; 16位模拟输入&#xff1a; 16路单端通道或者8路差分通道。每个通道可编程的范围&#xff1a;-1V-1V&#xff0c;-2V-2V&#xff0c;-5V-5V&#xff0c;-10V-10…

经典数据结构之2-3树

2-3树定义 2-3树&#xff0c;是最简单的B-树&#xff0c;其中2、3主要体现在每个非叶子节点都有2个或3个子节点&#xff0c;B-树即是平衡树&#xff0c;平衡树是为了解决不平衡树查询效率问题&#xff0c;常见的二叉平衡书有AVL树&#xff0c;它虽然提高了查询效率&#xff0c…

Mac AndroidStudio开发环境搭建

Mac AndroidStudio开发环境搭建 1.在Mac上配置环境变量 执行 open ~/.bash_profile (该文件是配置环境变量&#xff09; 如果提示没有 执行 touch ~/.bash_profile 进行创建 打开文件之后 新增jdk和android sdk 参考蓝色部分需要替换对应内容 #jdk export JAVA_HOME/Library/J…