python 遗传编程_遗传算法Python实战 006.扑克牌问题

本文介绍了使用遗传算法解决扑克牌排列组合问题,目标是将10张牌分为两组,使得一组数字之和为36,另一组数字之积为360。通过面向对象设计,创建了表示健壮性的类,探讨了增加基因突变以加速进化的策略。文章还引入了模因遗传算法的概念,将其与经典遗传算法进行对比,并引用了模因的含义和传播。
摘要由CSDN通过智能技术生成

写在前面的话

前面五个问题,在遗传算法里面,都是非常简单入门问题,如果能够读懂前面这些算法,而且能够复现的话,说明你对遗传算法已经完成了入门过程,到这里就开始进入遗传算法的中级阶段了。

好了,先看看本节需要解决的问题:

本节中,我们将通过排列组合扑克牌,来完成这样的一个游戏:

如果给你从A(就算成是1吧)到10,这十张扑克牌,让你把这10张牌分成两组,第一组的数字之和,加起来等于36,第二组的数字之乘积,乘起来等于360。注意,十张牌,每张牌只能用一次,不得重复。

比如组合成下面这一组组合:(这几个数字是唯一的解法,但是如果是扑克牌的话,可以有不同的排列方式)

  • 第一组:

6de4cfe705eeaf5491f1c83152b938a4.png

2 + 7 + 8 + 9 + 10 = 36

  • 第二组:

469c26d17afcfee2989ead774c6abd4d.png

1 * 3 * 4 * 5 * 6 = 360

算法实现部分

首先还是导入包和构建基因库:

# 注意,这里新导入的两个包functools和operator是Python的两个默认库
# 是Python实现函数式编程的核心包——有兴趣的同学去了解一下函数式编程
# 虾神评价:很神奇……
import random,datetime,functools,operator
geneset = [i +1 for i in range(10)]

接下去构建健壮性类,这个类的作用是标识这次进化的健壮性的

实际上到今天为主,大家已经看出来了,作者是面向对象时代过来人,很多功能都喜欢直接用对象来进行描述,这样有其的好处,就是封装性非常好,所有的功能属性一个对象就全部包含了,但是对于写惯了标准Python编程脚本的人,有些不适应。

我们可以简单看看这个类的设计:

  • 首先是一个构造函数,设计了三个成员变量:

    • Group1Sum用关于记录第一组分组的和

    • Group2Product用于记录第二组的乘积

    • TotalDifference用于记录两组的结果与最终期望结果的差距。

  • 然后重写了两个比较函数,第一个是小于,仅比较真实结果与期望结果的的效果。第二个就是等于,用于比较真实结果是否等于期望结果。

  • 最后是一个字符串转换函数,输出两组分组的结果(第一组的和,第二组的乘积)

class Fitness:
def __init__(self, group1Sum, group2Product):
self.Group1Sum = group1Sum
self.Group2Product = group2Product
sumDifference = abs(36 - group1Sum)
productDifference = abs(360 - group2Product)
self.TotalDifference = sumDifference + productDifference

def __gt__(self, other):
return self.TotalDifference < other.TotalDifference

def __eq__(self,other):
return self.TotalDifference == other.TotalDifference

def __str__(self):
return "sum: {} prod: {}".format(self.Group1Sum,
self.Group2Product)

然后是核心函数:健壮性检验:这里面的乘积方法,用了一个函数式编程的一个经典函数reduce(会hadoop或者Spark的同学,肯定很熟悉了),不过reduce的效率是不是比传统for循环自己写乘积方法快,还有待商榷(起码我这里,reduce方法比自己写for要慢)

def get_fitness(genes):
group1Sum = sum(genes[0:5])
group2Product = functools.reduce(operator.mul, genes[5:10])
return Fitness(group1Sum, group2Product)

显示函数,不解释:

def display(candidate, startTime):
timeDiff = datetime.datetime.now() - startTime
print("{} - {}\t{}\t{}".format(
', '.join(map(str, candidate.Genes[0:5])),
', '.join(map(str, candidate.Genes[5:10])),
candidate.Fitness,
timeDiff))

种群类,不解释:

class Chromosome:
def __init__(self, genes, fitness):
self.Genes = genes
self.Fitness = fitness

进化函数

在看本次的进化函数之前,先回想一下前面五节的进化函数,可以发现基本都是一样的,都是直接随机从基因库中提取一个基因,替换掉种群中的一个基因,这样变成了一个新的物种……这也是遗传算法里面的基本原理。

这里的进化函数,发生了一点点变化,并不仅仅是去变化一个(或者一组)基因,而是多去替换几组基因——比如我之类设置的4组,就是所谓的替换四组基因。

——这就是进化中另外一个核心的理论:我们可以加大每次基因的突变,这样有可能会加速进化的历程,不过是不是基因突变越大,进化越快,效果就越好呢?这个问题还是值得讨论的(大部分时候都不是)

def mutate(parent, geneset):
genes = parent.Genes[:]
for i in range(4):
indexA, indexB = random.sample(range(len(genes)), 2)
genes[indexA], genes[indexB] = genes[indexB], genes[indexA]
fitness = get_fitness(genes)
return Chromosome(genes, fitness)

然后是执行函数了:

流程与以往是一样的:初始化初代种群,如果初代种群就是最佳种群了,自然就结束进化了,否则进入进化流程——每次都用上一代最优的种群最为父本基因扔到进化函数里面去进化,之后对比进化出来的种群的健壮性是否更优秀,如果是,则替换父本,否则不变——直到进化到我们理想的目标为止:

def get_best():
startTime = datetime.datetime.now()
optimalFitness = Fitness(36, 360)
best = Chromosome(geneset, get_fitness(geneset))
if best.Fitness == optimalFitness:
return best
num = 0
while True:
num +=1
child = mutate(best,geneset)
if best.Fitness > child.Fitness:
continue
display(child,startTime)
if not child.Fitness > best.Fitness:
best = child
if child.Fitness == optimalFitness:
break
best = child
return (best,num)

这里用了一个num来计数,看看每次进化需要多少代,执行100次,生成的分布如下:

75929b39589075602ea40b330cbeb3d4.png

如果前面那些不变的进化算法被称为“经典基因遗传算法”,这种自定义突变的算法,被赋予了一个新的名称:“模因遗传算法”,用英文来表达就是“Memetic algorithms”,而Memetic这个词,是专门针对genetic(基因)这个词被创造出来的,目前在学术界还没有权威的翻译,一般而言,以英国演化理论学者理查德·道金斯的著作《自私的基因》为准,被翻译为模因的,比较多。

模因被认为是一种可以快速自我复制的文化基因,它存在于我们的大脑中,并且能通过模仿实现自我复制。它可以是一首歌曲、一本书的内容或者某些人的观点。这些模因通过人类的大脑进行复制传播,就好比基因通过身体复制传播一样。

下面是对于模因一段非常经典的解释:

当某一个人接触愤怒太久,他就会学会恨。他就会变成一个携带者,将嫉妒,贪婪,绝望… 将所有这些文化基因都传递下去. ——合金装备•崛起

那么这种进化算法,为什么会被叫做模因算法呢?我们后面会慢慢揭晓。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值