余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

遗传算法(Python) #3 从零开始解决OneMax问题

xiyangw 2023-09-17 16:23 6 浏览 0 评论

上一期

1. OneMax问题(OneMax Problem)

OneMax问题是遗传算法的入门问题,其内容是:如何使一段长度固定的二进制字符串所有位置上数字之和最大。

让我们用一个长度为5的二进制字符串为例:

  • 10010 -> 和为2
  • 00111 -> 和为3
  • 11111 -> 和为5(最大值)

对一般人,显而易见,当所有位数都为1时,该字符串的和最大,但在我们用遗传算法解决该问题时,遗传算法本身并没有这样的知识。接下来我们将不依靠任何遗传算法的包,从头开始用遗传算法解决OneMax问题。

2.问题的解决思路

首先,我们得把这个问题转换成一个遗传算法问题,即:我们得定义个体、种群,选择、杂交、突变方法、适应度函数等。假设有一个长度为100的字符串,我们可以做出以下定义:

  • 个体:个体即为问题的解,这个问题中个体可以直观的定义为一个长度为100列表(List),列表上每个元素为0或1.
  • 种群:种群即所有个体的合集,我们可以把种群定义为所有个体组成的列表。
  • 选择:使用锦标赛法(Tournament Selection)
  • 杂交:使用单点杂交法(Singe-Point Crossover)
  • 突变:使用位翻转突变法(Flip Bit Mutation)
  • 适应度函数: 我们的目标是使字符串上所有数字之和最大,适应度函数可以直观的定义为列表中所有数字之和。

若对上述定义不太了解的,可以回看遗传算法系列的第二期。

3.用Python实现遗传算法

以下将分步骤解释每一部分的代码,完整代码在本文的最后可见。

3.0.准备工作

import random
import matplotlib.pyplot as plt
random.seed(39)

导入所有需要的包:

  • random: 用于生成随机数
  • matplotlib: 用于画图
  • random.seed: 确定种子(即随机状态),这样每次运算我们都能得到同样的结果

3.1.定义个体和种群

# 1. define individual and population
def CreateIndividual():
    return([random.randint(0,1) for _ in range(100)])

def CreatePopulation(size):
    return([CreateIndividual() for _ in range(size)])
  • CreateIndividual:个体即为一个长度为100的列表,每个元素(基因)为0或1。
  • CreatePopulation: 种群即多个个体组成的列表。

3.2.定义选择、杂交、突变方法和适应度函数

3.2.1.选择

# 2.1. define select function
def tournament(population,size):
    participants = random.sample(population,size)
    # evaluate function is defined in 3.4
    winner = max(participants,key=lambda ind:evaluate(ind))
    return(winner.copy())

def select(population,size):
    return([tournament(population,size) for _ in range(len(population))])
  • tournament:从种群中随机抽取size个个体,从中选取适应度最高的个体。注意使用.copy以防同一个列表被多次引用。
  • select:为了保持种群的大小不变,锦标赛的次数应与种群中个体的数量相同

3.2.2.杂交

# 2.2. define mate function
def SinglePointCrossover(ind1,ind2):
    loc = random.randint(0,len(ind1)-1)
    genes1 = ind1[loc:]
    genes2 = ind2[loc:]
    ind1[loc:] = genes2
    ind2[loc:] = genes1
    return([ind1.copy(),ind2.copy()])

def mate(population,probability):
    new_population = []
    for i in range(0,len(population),2):
        ind1 = population[i].copy()
        ind2 = population[i+1].copy()
        if random.random() < probability:
            new_population.extend(SinglePointCrossover(ind1,ind2))
        else:
            new_population.extend([ind1,ind2])
    return(new_population)
  • SinglePointCrossover:单点杂交法的实现,两个个体交换随机位置及其后面的基因
  • mate:对种群进行杂交,按一定概率每两个相邻的个体进行杂交probability参数:每次生成一个0至1之间的随机数,若随机数小于该概率,则进行杂交,否则对应的两个个体被克隆。

3.2.3.突变

# 2.3. define mutate function
def flipOneGene(ind):
    loc = random.randint(0,len(ind)-1)
    ind[loc] = 1 - ind[loc] # 0->1 or 1->0
    return(ind.copy())

def mutate(population,probability):
    new_population = []
    for ind in population:
        if random.random() < probability:
            new_population.append(flipOneGene(ind))
        else:
            new_population.append(ind.copy())
    return(new_population)
  • flipOneGene:位翻转突变,个体上的一个随机基因进行突变。
  • mutate: 每个个体都以一定概率突变,若不突变,则该个体被克隆。

3.2.4.适应度函数

# 2.4. define evaluate function
def evaluate(individual):
    return(sum(individual))

OneMax的适应度函数就是列表中所有数字之和。

3.2.5.统计种群数据

# 2.5. define statistical metrics to monitor algorithm performance
def population_score_max(population):
    return(max([evaluate(ind) for ind in population]))

def population_score_mean(population):
    return(sum([evaluate(ind) for ind in population])/len(population))

为了追踪算法的进度和发现算法中可能出现的错误,我们可以统计每次迭代中种群适应度的最大值与均值。

3.3.运行遗传算法

# 3. Run genetic algorithm
def main(
    POPULATION_SIZE = 100,
    TOURNAMENT_SIZE = 3,
    CROSSOVER_PROB = 0.9,
    MUTATE_PROB = 0.1,
    MAX_GENERATIONS = 100
):
    generation = 0
    population = CreatePopulation(POPULATION_SIZE)
    max_scores = [population_score_max(population)]
    mean_scores = [population_score_mean(population)]
    best_individual = []
    while generation < MAX_GENERATIONS:
        population = select(population,TOURNAMENT_SIZE)
        population = mate(population,CROSSOVER_PROB)
        population = mutate(population,MUTATE_PROB)
        # collect statistics
        max_scores.append(population_score_max(population))
        mean_scores.append(population_score_mean(population))
        best_individual = max(
            best_individual,
            max(population,key=lambda ind: evaluate(ind))
        ).copy()
        generation += 1
    
    print("Best Solution:")
    print(best_individual)
    plt.plot(max_scores, color='red',label="Max Score")
    plt.plot(mean_scores, color='green',label="Mean Score")
    plt.legend()
    plt.xlabel("Generations")
    plt.ylabel("Fitness Score")
    plt.grid()
    plt.show()   

if __name__ == "__main__":
    main()

在运行算法前,我们首先得定义一些参数:

  • 种群大小(Population Size):一般而言种群越大,越容易在较小的代数内获得最优解,但代价是每一代的运算量会变大。
  • 锦标赛大小(Tournament Size): 锦标赛的大小一般不要取的太大,否则种群容易因过早地失去多样性而过早收敛。举一个极端例子,当种群大小为100且锦标赛大小也为100时,第一次选择后所有的个体都会变成同一个个体,算法便很难再有改善的空间。
  • 杂交概率(Crossover Probability):杂交的概率应取一个较大的值,比如0.7,0.8,0.9等,具体数值应视具体情况而定。
  • 突变概率(Mutate Probability):突变概率应取一个较小的值,如0.05,0.1,0.2等,若取值太大,则种群的随机性太强,不利于保存适应度高的个体,从而使遗传算法变成完全随机的搜索。
  • 最大代数(Max Generation):遗传算法不可能无止尽的运行下去,一般而言,我们都会设置终止条件,这个问题中,我们以最大代数为终止条件,当迭代进行到一定次数时算法终止。

在迭代过程中,每一代里我们都进行选择(select),杂交(mate),突变(mate)运算,并收集种群的最大和平均适应度数据,用以追踪算法的进度,或发现算法中存在的问题。视问题而定,我们还可以记录下每代中适应度最高的个体(best individual),以防止其因杂交和突变而消失。

最后,我们观察最优解与种群的数据。

4.算法结果

我们成功获得了OneMax的最优解(所有位置上都是1):[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

种群的进化过程如下图所示,我们可以观察到种群的进化速度由快到慢,且在61代左右停止进化,且在第58代时已经产生了最优解。



5. 完整代码

import random
import matplotlib.pyplot as plt
random.seed(39)
# 1. define individual and population
def CreateIndividual():
    return([random.randint(0,1) for _ in range(100)])

def CreatePopulation(size):
    return([CreateIndividual() for _ in range(size)])

# 2. define select, mate, mutate, evaluate function ----
# 2.1. define select function
def tournament(population,size):
    participants = random.sample(population,size)
    # evaluate function is defined in 3.4
    winner = max(participants,key=lambda ind:evaluate(ind))
    return(winner.copy())

def select(population,size):
    return([tournament(population,size) for _ in range(len(population))])

# 2.2. define mate function
def SinglePointCrossover(ind1,ind2):
    loc = random.randint(0,len(ind1)-1)
    genes1 = ind1[loc:]
    genes2 = ind2[loc:]
    ind1[loc:] = genes2
    ind2[loc:] = genes1
    return([ind1.copy(),ind2.copy()])

def mate(population,probability):
    new_population = []
    for i in range(0,len(population),2):
        ind1 = population[i].copy()
        ind2 = population[i+1].copy()
        if random.random() < probability:
            new_population.extend(SinglePointCrossover(ind1,ind2))
        else:
            new_population.extend([ind1,ind2])
    return(new_population)
# 2.3. define mutate function
def flipOneGene(ind):
    loc = random.randint(0,len(ind)-1)
    ind[loc] = 1 - ind[loc] # 0->1 or 1->0
    return(ind.copy())

def mutate(population,probability):
    new_population = []
    for ind in population:
        if random.random() < probability:
            new_population.append(flipOneGene(ind))
        else:
            new_population.append(ind.copy())
    return(new_population)
# 2.4. define evaluate function
def evaluate(individual):
    return(sum(individual))

# 2.5. define statistical metrics to monitor algorithm performance
def population_score_max(population):
    return(max([evaluate(ind) for ind in population]))

def population_score_mean(population):
    return(sum([evaluate(ind) for ind in population])/len(population))

# 3. Run genetic algorithm
def main(
    POPULATION_SIZE = 100,
    TOURNAMENT_SIZE = 3,
    CROSSOVER_PROB = 0.9,
    MUTATE_PROB = 0.1,
    MAX_GENERATIONS = 100
):
    generation = 0
    population = CreatePopulation(POPULATION_SIZE)
    max_scores = [population_score_max(population)]
    mean_scores = [population_score_mean(population)]
    best_individual = []
    while generation < MAX_GENERATIONS:
        population = select(population,TOURNAMENT_SIZE)
        population = mate(population,CROSSOVER_PROB)
        population = mutate(population,MUTATE_PROB)
        # collect statistics
        max_scores.append(population_score_max(population))
        mean_scores.append(population_score_mean(population))
        best_individual = max(
            best_individual,
            max(population,key=lambda ind: evaluate(ind))
        ).copy()
        generation += 1
    
    print("Best Solution:")
    print(best_individual)
    plt.plot(max_scores, color='red',label="Max Score")
    plt.plot(mean_scores, color='green',label="Mean Score")
    plt.legend()
    plt.xlabel("Generations")
    plt.ylabel("Fitness Score")
    plt.grid()
    plt.show()   

if __name__ == "__main__":
    main()

4.小结

为了深入的解释遗传算法,本文中没有使用任何的遗传算法包来解决OneMax问题。而下一期,我们会介绍DEAP(Distributed Evolutionary Algorithm in Python)包,并在之后的文章里用DEAP框架来解决遗传算法问题。

本人近期刚开始写文章,欢迎交流学习!

相关推荐

Mac软件删除方法,这样删除不会有残留
Mac软件删除方法,这样删除不会有残留

Mac电脑如果有太多无用的应用程序,很有可能会拖垮Mac系统的运行速度。因此,卸载电脑中无用的软件是优化Mac系统运行速度的最佳方式之一。Mac卸载应用程序的方...

2023-09-23 17:34 xiyangw

安利一款 Mac 的清理工具 Cleaner One
安利一款 Mac 的清理工具 Cleaner One

自从入手mac以后,一直在找款mac的清理工具之前也尝试过CleanMyMac和柠檬清理柠檬清理是腾讯旗下的,虽然免费,但更新不频繁,最近一次更新还...

2023-09-23 17:33 xiyangw

苹果电脑需要安装杀毒软件吗?一文告诉你
苹果电脑需要安装杀毒软件吗?一文告诉你

随着数字时代的发展,计算机安全问题变得越来越重要。而在计算机安全领域中,杀毒软件是一个被广泛讨论的话题。苹果电脑需要安装杀毒软件吗?对于苹果电脑用户来说,他们常...

2023-09-23 17:30 xiyangw

mac上实用的工具

mac系统上有很多好用的工作,本期给大家带来一些本人长期使用的软件,特别是刚从windows系统的pc转移到macbook上的小伙伴,可能有一定的帮助。1.Alfred可以完全取代苹果自带Spotl...

Mac专用免费清理软件CleanMyMac
Mac专用免费清理软件CleanMyMac

在Mac中,越来越多的垃圾占用了磁盘空间怎么办?直接拖拽到废纸篓很多软件不能完全卸载干净怎么办……伴随着这些问题,如果有一款多功能的软件能够解决以上的全部难题就...

2023-09-23 17:29 xiyangw

MAC软件分享CleanMyMac中文版 支持最新版系统
MAC软件分享CleanMyMac中文版 支持最新版系统

CleanMyMac的强大不需要过多的去介绍,软件支持最新版Macos10.15.6系统。CleanMyMac具有非常强大的功能,可让您安全,智能地扫描和清理...

2023-09-23 17:28 xiyangw

安装CleanMyMac 3提示软件已损坏
安装CleanMyMac 3提示软件已损坏

安装CleanMyMac3提示软件已损坏,出现这样的原因是往往是使用了CleanMyMac3破解版,主要是因为CleanMyMac3的来源问题,我们的正版软件(...

2023-09-23 17:27 xiyangw

苹果Mac中使用 CleanMyMac X 清理垃圾时频繁要求输入密码如何解决?
苹果Mac中使用 CleanMyMac X 清理垃圾时频繁要求输入密码如何解决?

有不少用户反映在使用CleanMyMac清理系统垃圾文件的时候会频繁要求输入开机密码,如何解决这个问题?来看看吧!解决方法:1.打开「终端」,并输入以下命令...

2023-09-23 17:27 xiyangw

MacBook清理垃圾软件哪个好
MacBook清理垃圾软件哪个好

很多时候手动清理mac效果并没有那么好,常常会有疏忽的地方,其实我们完全可以依赖一些mac清理垃圾软件。windows上我们会借助360安全卫士、腾讯安全管家等...

2023-09-23 17:26 xiyangw

全球真的只有13台DNS根域名服务器吗?
全球真的只有13台DNS根域名服务器吗?

DNS根域名服务器(DNSrootnameservers)是一组特殊的DNS服务器,它们存储有关Internet域名系统(DNS)中所有顶级域的信息。这些...

2023-09-23 17:25 xiyangw

网络管理员,网络工程师每日一练

在DNS服务器中的()资源记录定义了区域的邮件服务器及其优先级。A.SOAB.NSC.PTRD.MX试题答案:D...

Android性能优化之网络优化DNS和HttpDNS知识详解
Android性能优化之网络优化DNS和HttpDNS知识详解

前言小计在App访问网络的时候,DNS解析是网络请求的第一步,默认我们使用运营商的LocalDNS服务。有数据统计,在这一块3G网络下,耗时在2...

2023-09-23 17:25 xiyangw

如何修改域名DNS服务器?修改DNS服务器常见问题汇总
如何修改域名DNS服务器?修改DNS服务器常见问题汇总

在域名管理过程中,我们为了获得更专业安全的域名解析服务,就需要修改DNS服务器,下面中科三方针对修改DNS服务器常见问题做下简单回答。1.修改DNS服务器和修改...

2023-09-23 17:24 xiyangw

netty系列之:在netty中使用 tls 协议请求 DNS 服务器

简介在前面的文章中我们讲过了如何在netty中构造客户端分别使用tcp和udp协议向DNS服务器请求消息。在请求的过程中并没有进行消息的加密,所以这种请求是不安全的。那么有同学会问了,就是请求解析一个...

「GCTT 出品」使用 Golang 构建 DNS 服务器
「GCTT 出品」使用 Golang 构建 DNS 服务器

需求:对DNS查询进行转发和缓存的本地DNS服务器。补充1:提供一个记录管理的接口(HTTPhandler)。补充2:提供一个名字(name)。D...

2023-09-23 17:23 xiyangw

取消回复欢迎 发表评论: