启发式搜索算法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.mzph.cn/diannao/5778.shtml

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

相关文章

第 8 章 机器人平台设计(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 学习到当前阶段大家对ROS已经有一定的认知了&#xff0c;但是之前的内容更偏理论&#xff0c;尤其是介绍完第6…

Excel 透视表:数据分析利器

Excel 透视表&#xff1a;数据分析利器 Excel 透视表是 Excel 中一个强大的数据分析工具&#xff0c;可以帮助用户快速、轻松地汇总、分析大量数据。它能够将复杂的数据转化为易于理解的摘要信息&#xff0c;并提供多种视图和选项供用户探索数据。 重要性 Excel 透视表在数据…

大数据信用花了,一般多久能正常?

在当今数字化时代&#xff0c;大数据技术被广泛应用于各个领域&#xff0c;包括金融、电商、社交等。然而&#xff0c;随着大数据技术的普及&#xff0c;个人信用问题也日益凸显&#xff0c;其中“大数据信用花”现象尤为引人关注。那么&#xff0c;大数据信用花究竟是什么?一…

【Unity】修改模型透明度

在 Unity 中修改模型透明度主要有两种方法&#xff1a;通过材质和通过着色器。以下是两种方法的步骤和解释&#xff1a; 方法 1&#xff1a;通过材质 在 Unity 编辑器中&#xff0c;选择你想要修改透明度的模型。在 Inspector 窗口中&#xff0c;找到模型的 Renderer 组件&am…

HTML5本地存储账号密码

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>HTML5本地存储账号密码</title> </head…

【Java从入门到精通】Java方法

在前面几个章节中我们经常使用到 System.out.println()&#xff0c;那么它是什么呢&#xff1f; println() 是一个方法。System 是系统类。out 是标准输出对象。 这句话的用法是调用系统类 System 中的标准输出对象 out 中的方法 println()。 那么什么是方法呢&#xff1f; …

主成分分析在R语言中的简单应用:使用mvstats包

在数据科学领域&#xff0c;主成分分析&#xff08;PCA&#xff09;是一种广泛使用的技术&#xff0c;主要用于数据降维和探索性数据分析。PCA可以帮助我们发现数据中的模式&#xff0c;减少数据集的复杂性&#xff0c;同时保持数据中最重要的特征。本文将介绍如何在R语言中使用…

04_jvm性能调优_并行收集器介绍

并行收集器&#xff08;此处也称为吞吐量收集器&#xff09;是类似于串行收集器的分代收集器。串行和并行收集器之间的主要区别在于并行收集器具有多个线程&#xff0c;用于加速垃圾回收过程。 通过命令行选项-XX:UseParallelGC 可启用并行收集器。默认情况下&#xff0c;使用…

LeetCode 105.从前序与中序遍历构造二叉树

题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,nul…

【LeetCode刷题记录】110. 平衡二叉树

110 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是平衡二叉树 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false 示例 3&…

vue3项目引入VueQuill富文本编辑器(成功)及 quill-image-uploader 图像模块(未成功)

tip&#xff1a;重点解释都写在代码注释里了&#xff0c;方便理解&#xff0c;所以看起来比较密集 富文本基本使用 项目文件夹路径安装依赖 npm install vueup/vue-quilllatest --save 全局注册&#xff1a;main.js // main.js// 自己项目的一些配置&#xff08;只放了主要…

注册表获取autoCAD安装位置

注意事项 注意&#xff1a;①64位操作系统注册表会重定向&#xff0c;RegOpenKeyEx第4个参数得加上KEY_WOW64_64KEY&#xff1b;②RegOpenKeyEx遍历子项时注意第2和第4参数&#xff0c;参考图&#xff1a; ③RegQueryValueEx同样得注意第6参数 完整代码 std::unordered_map…

基于ssm+vue+Mysql的药源购物网站

开发语言&#xff1a;Java框架&#xff1a;ssmJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;Maven3.…

Cokejogo巴西 电子游戏源码 游戏网站源码 电子游戏合集 电子游戏软件下载 游戏源码(带安装教程)

Cokejogo巴西pg电子游戏源码/H5PC端 前端vue编译后后端PHP/修复图片资源失效 后端测试环境&#xff1a;Linux系统CentOS7.6、宝塔、PHP7.2、MySQL5.6&#xff0c;根目录public&#xff0c;伪静态thinkPHP&#xff0c;开启ssl证书 源码下载&#xff1a;https://download.csdn.n…

CUDA内存模型

核函数性能并不只与线程束的执行有关。 CUDA内存模型概述 GPU和CPU内存模型的主要区别是&#xff0c;CUDA编程模型能将内存层次结构更好地呈现给用户&#xff0c;能让我们显示的控制它的行为。 对程序员来说&#xff0c;一般有两种类型的存储器&#xff1a; 可编程的&#x…

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

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

企业职能部门定岗定编项目如何做?

某国家级高新技术开发区成立于上世纪90年代&#xff0c;位于南方某市&#xff0c;地处三省交界处&#xff0c;是直接由该省委省政府创办的全省首批高新技术开发区。该开发区面积达到300平方公里&#xff0c;辖区人口30万人。历经数十年发展&#xff0c;在省委省政府的高度重视下…

QT:核心控件-QWidget

文章目录 控件enableobjectNamegeometrysetWindowTitleopacitycursorFonttooltipstyleSheet 控件 什么是控件&#xff1f; 如上所示&#xff0c;就是控件&#xff0c;而本篇要做的就是对于这些控件挑选一些比较有用的常用的进行讲解分析 在QT的右侧&#xff0c;会有对应的空间…

unity制作app(1)--登录 注册 界面

把学到的知识投入到生产中反而是一件简单的事情&#xff01; 1.调整canvas的形状&#xff0c;这里和camera没有任何关系! overlay&#xff01; 2.既然自适应&#xff0c;空间按钮的位置比例就很重要了&#xff01; game窗口中新增720*1280的分辨率&#xff01; 3.再回到can…

【论文阅读】ViTAE:Vision transformer advanced by exploring intrinsic inductive bias

ViTAE:Vision transformer advanced by exploring intrinsic inductive bias 论文地址摘要&#xff1a;简介&#xff1a;3 方法论3.1 重温视觉变压器3.2 ViTAE3.3 缩减单元3.4 Normal cell3.5 模型细节 4 训练4.1 Implementation details4.2 Comparison with the state-of-the-…