启发式搜索算法4 -遗传算法实战:吊死鬼游戏

相关文章:
启发式搜索算法1 – 最佳优先搜索算法
启发式搜索算法2 – A*算法
启发式搜索算法2 – 遗传算法

有一个小游戏叫吊死鬼游戏(hangman),在学习英语的时候,大家有可能在课堂上玩过。老师给定一个英文单词,同学们就猜是什么单词,猜错一次老师就画一笔,如果把吊死鬼画出来就没有机会猜了,游戏结束。现在我们不限猜测的次数,让电脑也来玩一下,看它要多久才能猜中。

1 把背景剥离,找出问题核心,简化问题。

对于计算机来说,最简单就是盲目搜索,穷举所有情况,直到猜中为止。这样的穷举算法有兴趣的读者可以尝试用代码实现。这里当然要用刚学习的遗传算法,根据前面的介绍的算法思路,结合实际情况,来设置几个关键要素。

  • 字符从a至z视为基因,由这些字符生成的字符串被认为是染色体(个体),也就是一个解。
  • 适应能力得分就是用个体字符串与目标字符串比较,相同位置有相同字符的个数。
    因此,具有较高适应值的个体(猜中更多字母的解)将获得更多的繁殖权力。
  • 设置种群大小(一代个体的个数),初始种群的数量很重要,如果初始种群数量过多,算法会占用大量系统资源;如果初始种群数量过少,算法很可能忽略掉最优解。这次我们设置种群的数量为100,同一代会有一百个体。
  • 设置下一代组成,适应能力前10%直接进入下一代,这称为精英模式,适应能力前50%的个体拥有繁衍权力,则交配概率为50%,也就是必然产生下一代。一般取较大的交配概率,因为交配操作可以加快解区间收敛,使解达到最有希望的最佳解区域,但交配概率太高也可能导致过早收敛,则称为早熟,只找到局部最优解就停止进化。还有突变概率,这里设置为0.1,也可以说在组合新一代的每一个基因,有45%来自父亲基因,有45%来自母亲基因,有10%发生变异,随机产生一个基因。
  • 终止条件是适应能力值等于单词长度,也就是找到了目标单词,又或者到达最大进化代数(自定义参数配置),也会终止程序。

2.估算数据规模,算法复杂度

如果用穷举算法,可以估算时间复杂度,每一个位置尝试一遍26个字母,所以时间复杂度O(26^n),n为单词的长度。但对于遗传算法来说,由于太多不确定性,遗传算法的精度、可行度、计算复杂性等方面参数还没有有效的定量分析方法,这也算是它的一个缺点。

import random
GENES = 'abcdefghijklmnopqrstuvwxyz'  # 基因
class Individual(object): '''此类代表种群中的个体'''def __init__(self, target, chromosome=None):self.target = targetif chromosome: # 个体染色体,也就是所猜的单词解self.chromosome = chromosomeelse: # 若没有,创建一个随机的染色体self.chromosome = self.create_gnome()self.fitness = self.cal_fitness()  # 此个体的适用能力值@classmethod # 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数def mutated_genes(cls):# 基因突变,也就是从a-z中随机挑选一个字母global GENES gene = random.choice(GENES) return genedef create_gnome(self):# 初始化个体基因gnome_len = len(self.target) # 根据目标单词长度随机构建一个字符串return [self.mutated_genes() for _ in range(gnome_len)]def mate(self, parent, mutation=0.1):# 进行繁衍,产生新一代child_chromosome = [] # 后代染色体p1_proba = (1 - mutation) / 2 # 取得父母基因概率是一样for p1, p2 in zip(self.chromosome, parent.chromosome):# 遍历父母每一个基因,通过一定概率随机获取父母其中一方的基因,还有0.1概率发生变异prob = random.random() #获取一个0-1的随机数if prob < p1_proba: # 一半概率选择选择p1child_chromosome.append(p1) elif prob < p1_proba*2: # 一半概率选择选择p2child_chromosome.append(p2) else: # 剩下1-probability概率发生基因突变child_chromosome.append(self.mutated_genes()) # 创建一个新个体,并返回return Individual(self.target, chromosome=child_chromosome) def cal_fitness(self): # 计算适应能力值,记录正确的字符数量fitness = 0for gs, gt in zip(self.chromosome, self.target): if gs == gt: fitness+= 1return fitness
class GeneticAlgorithm(object):'''遗传算法'''def __init__(self, target, population_size=100,proba_elitism=10, proba_crossover=50,mutation=0.1, max_generation=100):self.population_size = population_size  # 种群大小self.target = target  # 目标单词self.proba_elitism = proba_elitism  # 精英模式的比例self.proba_crossover = proba_crossover  # 交配概率self.mutation = mutation  # 突变概率self.max_generation = max_generation  # 最大进化代数,也就是循环最多次数self.found = False  # 初始化时没有找到最优解self.generation = 1  # 当前进化的世代,创世纪是1self.population = []  # 种群,初始化为空def init_population(self):# 初始化第一代个体for _ in range(self.population_size):self.population.append(Individual(self.target))def main(self):# 主函数self.init_population() # 产生第一代个体# 若没有找到答案,并且没有达到最大进化代数,继续下一代演化while not self.found and self.generation < self.max_generation: # 按照适应能力排序-内置排序法self.population = sorted(self.population, key = lambda x:x.fitness, reverse=True)# 一旦发现有适应值和目标长度一样,说明我们找到这个单词if self.population[0].fitness == len(self.target): self.found = Truebreak# 记录下一代个体new_generation = []# 精英模式,选择前10%的个体进入下一代s = int((self.proba_elitism*self.population_size)/100) new_generation.extend(self.population[:s]) # 90%的个体是通过上一代的交配得到s = int(((100 - self.proba_elitism)*self.population_size)/100)# 前50%有繁衍下一代的个体数量num_crossover =  int(self.proba_crossover * self.population_size / 100)for _ in range(s):# 前50%的个体可以有繁衍权力,在这些个体中随机挑选两个parent1 = random.choice(self.population[:num_crossover])  parent2 = random.choice(self.population[:num_crossover])child = parent1.mate(parent2, mutation=self.mutation) # 进行繁衍new_generation.append(child) # 得到新的一代个体self.population = new_generation # 新一代的个体代替上一代的print("第{}代\t单词: {}\t适应值: {}".format(self.generation, "".join(self.population[0].chromosome), self.population[0].fitness)) self.generation += 1 # 记录进化的代数print("第{}代\t单词: {}\t适应值: {}".format(self.generation, "".join(self.population[0].chromosome),self.population[0].fitness))

这里有两个类【Individual】是代表个体,【GeneticAlgorithm】是代表遗传算法。【Individual】个体在实例化的时候会先判断是否有染色体,如果没有就通过create_gnome()函数随机生成一个染色体,然后马上通过cal_fitness()函数计算它的适应能力值。mate()函数负责繁衍下一代新个体和mutated_genes()函数负责模拟基因突变。【GeneticAlgorithm】在实例化的时候就要设定遗传算法的各种配置,其中目标单词【target】是必要参数,其他参数是可选参数,都已经设定了默认值。然后通过调用main()函数来启动算法运行,在运算过程中把每一代适应能力值最大的个体打印出来,一起观察种群的进化过程。现在通过一些例子来验证代码是否可行。

ga = GeneticAlgorithm('generation')
ga.main()
# ---------结果-----------
ga = GeneticAlgorithm('generation')
第1代     单词: eikehaviro  适应值: 3
第2代     单词: eikehaviro  适应值: 3
第3代     单词: toneibxinn  适应值: 4
第4代     单词: binagavion  适应值: 5
第5代     单词: binagavion  适应值: 5
第6代     单词: emnenahion  适应值: 6
第7代     单词: emnenahion  适应值: 6
第8代     单词: emnenahion  适应值: 6
第9代     单词: ltneratton  适应值: 7
第10代    单词: eenaration  适应值: 8
第11代    单词: eeneration  适应值: 9
第12代    单词: eeneration  适应值: 9
第13代    单词: generation  适应值: 10

从打印在屏幕的信息中看到刚开始的适应能力值非常低,通过不断进化,适应值也逐步上升,到第十三代就找到答案,粗略估算每次100个个体,那么一共尝试了13*100=13000次就找到结果,效果还是不错的。因为遗传算法是具有不定性,当然不可能每次都是在第十三代就能得到结果,大家可以多尝试就会知道。同时可以尝试调整其他默认参数,观察参与变化对结果有什么影响。比如下面改变种群的个体数量,结果会发生什么变化。

target = 'announcement'
for p in range(100, 1000, 100): # 种群大小,每次增加100generate = 0for _ in range(10): # 每一种种群经过10次运算ga = GeneticAlgorithm(target, population_size=p)ga.main()generate += ga.generation # 统计总共经过多少次进化print('种群数量为%d, 总进化代数为%d, 平均每次通过%.1f进化得到结果' % (p, generate, generate/10))
# ---------结果-----------
种群数量为100, 总进化代数为199, 平均每次通过19.9进化得到结果
种群数量为200, 总进化代数为144, 平均每次通过14.4进化得到结果
种群数量为300, 总进化代数为132, 平均每次通过13.2进化得到结果
种群数量为400, 总进化代数为128, 平均每次通过12.8进化得到结果
种群数量为500, 总进化代数为131, 平均每次通过13.1进化得到结果
种群数量为600, 总进化代数为121, 平均每次通过12.1进化得到结果
种群数量为700, 总进化代数为115, 平均每次通过11.5进化得到结果
种群数量为800, 总进化代数为118, 平均每次通过11.8进化得到结果
种群数量为900, 总进化代数为118, 平均每次通过11.8进化得到结果

从结果我们看到,适当增加种群的个体数量,可以更快地获得结果。对于其他参数,大家也可以通过同样的方法进行测试,以下是其他参数的测试结果汇总到表,每一次只改变一个参数的值,其他参数按默认值。
在这里插入图片描述
从结果中知道,突变概率确实不能太大,越大需要更多次进化,并且在大于0.35就不能求出结果,因为程序设置了最大迭代次数就100。再查看繁衍权力这个参数,当它在40时候,也就是种群前40%的个体能够有机会繁衍下一代,得到进化代数结果是最小的。在解决实际问题的时候大家也可以通过这样的调试方式,找到合适的参数值。此时再用调试过的参数,种群数是400,突变概率是0.15,繁衍权力是40,重新测试一下,观察程序是否真的能提高效率。

ga = GeneticAlgorithm('generation', population_size=400,proba_crossover=40, mutation=0.15)
ga.main()
# ---------结果-----------
第1代 单词: uedpwxdkon  适应值: 3
第2代 单词: uedpwxdkon  适应值: 3
第3代 单词: gefzrbvbin  适应值: 4
第4代 单词: gexewgticx  适应值: 5
第5代 单词: oeteravign  适应值: 6
第6代 单词: xeneratijn  适应值: 8
第7代 单词: xeneratijn  适应值: 8
第8代 单词: xeneratijn  适应值: 8
第9代 单词: generation  适应值: 10

看到调优过的参数起作用了,比刚才少了4代,在第九代就找到答案了。也许这只是一个意外,那么再来做一个更严谨的测试,同样运行10次,验证是否真实提高效率。

generate1 = 0
generate2 = 0
for _ in range(10): # 经过10次运算ga = GeneticAlgorithm(target)ga.main()generate1 += ga.generationga = GeneticAlgorithm(target, population_size=400,proba_crossover=40, mutation=0.15)ga.main()generate2 += ga.generation
print('默认参数:总进化代数为%d, 平均每次通过%.1f进化得到结果' % (generate1, generate1/10))
print('调优参数:总进化代数为%d, 平均每次通过%.1f进化得到结果' % (generate2, generate2 / 10))
# ---------结果-----------
默认参数:总进化代数为148, 平均每次通过14.8进化得到结果
调优参数:总进化代数为101, 平均每次通过10.1进化得到结果

完整代码可以查看:打开

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式之监听器模式ListenerPattern(三)

一、介绍 监听器模式是一种软件设计模式&#xff0c;在对象的状态发生改变时&#xff0c;允许依赖它的其他对象获得通知。在Java中&#xff0c;可以使用接口和回调机制来实现监听器模式。 二、代码实例 1、事件Event类 package com.xu.demo.listener;// 事件类 public class…

Python 与 TensorFlow2 生成式 AI(三)

原文&#xff1a;zh.annas-archive.org/md5/d06d282ea0d9c23c57f0ce31225acf76 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第七章&#xff1a;使用 GAN 进行风格转移 神经网络在涉及分析和语言技能的各种任务中正在取得进步。创造力是人类一直占有优势的领域&…

深入浅出TCP 与 UDP

&#x1f525; 引言 在互联网的广阔天地里&#xff0c;TCP&#xff08;Transmission Control Protocol&#xff09;和UDP&#xff08;User Datagram Protocol&#xff09;作为传输层的两大支柱&#xff0c;各自承担着不同的使命。下面这篇文章将带你从基础到进阶&#xff0c;全…

如何安全可控的进行跨区域数据交换,提高数据价值?

跨区域数据交换指的是在不同地理位置或不同网络环境下的数据传输和共享。随着数字化转型的加速&#xff0c;企业及组织越来越依赖于数据的流动来优化业务流程、增强决策制定和推动创新。然而&#xff0c;跨区域数据交换也带来了一系列的挑战和风险&#xff0c;主要包括&#xf…

天地图路径规划功能实现

目录 1、天地图路径规划2、路径规划3、参数说明4、Demo 1、天地图路径规划 天地图Web服务API为用户提供HTTP/HTTPS接口&#xff0c;即开发者可以通过这些接口使用各类型的地理信息数据服务&#xff0c;可以基于此开发跨平台的地理信息应用。 Web服务API对所有用户开放。使用本…

分享天某云对象存储开发的体验

最近体验了天某云对象存储的功能&#xff0c;作为一名资深开发者&#xff0c;开发体验差强人意&#xff0c;与阿里云存在一定的差距。 首先在开发文档上居然没有基于nodejs的代码示例&#xff0c;只有java,c#,go等的代码示例&#xff0c;虽然有javascript的&#xff0c;但那也只…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-5

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

计算机视觉——OpenCV 使用分水岭算法进行图像分割

分水岭算法 分水岭算法&#xff1a;模拟地理形态的图像分割 分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中&#xff0c;每个像素的灰度值被视作其高度&#xff0c;灰度值较高的像素形成山脊&#xff0c;即分水岭&#xff0c;而二值化阈值则相当于水平面&am…

Windows如何通过wsl2迅速启动Docker desktop的PHP的Hyperf项目容器?

一、安装WSL 什么是WSL&#xff1f; 官网&#xff1a;什么是WSL&#xff1f; Windows Subsystem for Linux (WSL) 是一个在Windows 10和Windows 11上运行原生Linux二进制可执行文件的兼容性层。 换句话说&#xff0c;WSL让你可以在Windows系统上运行Linux环境&#xff0c;而无需…

【Web】2024XYCTF题解(全)

目录 ezhttp ezmd5 warm up ezMake ez?Make εZ?мKε? 我是一个复读机 牢牢记住&#xff0c;逝者为大 ezRCE ezPOP ezSerialize ezClass pharme 连连看到底是连连什么看 ezLFI login give me flag baby_unserialize ezhttp 访问./robots.txt 继…

linux高性能服务器--Ngix内存池简单实现

文章目录 内存模型&#xff1a;流程图内存对齐code 内存模型&#xff1a; 流程图 内存对齐 对齐计算 要分配一个以指定大小对齐的内存&#xff0c;可以使用如下公式&#xff1a; 假设要分配大小为n&#xff0c;对齐方式为x&#xff0c;那么 size(n(x-1)) & (~(x-1))。 举个…

【分布式通信】NPKit,NCCL的Profiling工具

NPKit介绍 NPKit (Networking Profiling Kit) is a profiling framework designed for popular collective communication libraries (CCLs), including Microsoft MSCCL, NVIDIA NCCL and AMD RCCL. It enables users to insert customized profiling events into different C…

26.统一网关Gateway

网关的功能 1.身份认证&#xff0c;权限的校验。 2.服务的路由&#xff0c;负载均衡。用户请求被分配到哪一个微服务。一个微服务可以有多个实例&#xff0c;所以使用负载均衡。 3.请求限流。 springcloud网关实现有两种&#xff1a;gateway, zuul zuul是基于servlet实现的…

随笔Ubuntu上的的一些使用

Ubuntu简易使用 常用指令 cdlsmkdirrf -rm 路径 换源 备份镜像 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak编辑文件设置 sudo gedit /etc/apt/sources.list清华源 # 阿里源 deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe mul…

数据仓库Data Warehouse

数据仓库Data Warehouse 数仓是一种思想,数仓是一种规范,数仓是一种解决方案 1. 数据处理方式 数据处理大致可以分成两大类: 联机事务处理OLTP(on-line transaction processing)联机分析处理OLAP(On-Line Analytical Processing)1.1. OLTP OLTP的全称是On-line Transa…

YOLO系列改进,自研模块助力涨点

目录 一、原理 二、代码 三、添加到YOLOv5中 一、原理 论文地址:

手机空号过滤,提高工作效率

手机空号过滤在多个方面都具有重要的作用。 首先&#xff0c;它对于短信群发商和电话营销商来说至关重要。通过空号过滤&#xff0c;他们可以确保手机号码数据库的准确性和有效性。由于每天都有大量人群因各种原因更换手机号码&#xff0c;导致每个号段中的空号率和手机状态都…

蓝桥杯如何准备国赛?

目录 一、赛前准备 1、如何刷题&#xff0c;刷哪些题&#xff1f; 2、记录&#xff08;主要看个人习惯&#xff09; CSDN博客 写注释 3、暴力骗分 4、从出题人的角度出发&#xff0c;应该如何骗分 二、赛中注意事项 一、赛前准备 1、如何刷题&#xff0c;刷哪些题&…

【算法刷题 | 贪心算法05】4.27(K次取反后最大化的数组和、加油站)

文章目录 8.K次取反后最大化的数组和8.1题目8.2解法&#xff1a;贪心8.2.1贪心思路8.2.2代码实现 9.加油站9.1题目9.2解法&#xff1a;贪心9.2.1贪心思路9.2.2代码实现 8.K次取反后最大化的数组和 8.1题目 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数…

制作github.io学术个人主页

制作如图的学术个人主页。About me - Xianwen Ling’s Blog 学术个人主页是一个学者展示个人学术成果和研究方向的重要工具。个人主页可以集中展示学者的研究论文、出版物、演讲和发布的项目等学术成果&#xff0c;这样其他人可以更方便地了解和评估学者的研究贡献。个人主页可…