遗传算法 初步认识(一) 简介 开始个人理解 目标函数 先来看看 目标函数的的校验吧 看看 在 0 - 9的区间上的值分布 codeResult 算法流程 code 自问自答环节 下一步

对什么蚁群算法啦,遗传算法啦...... 很感兴趣,遂学习一波
遗传算法真的是一个美妙的东西,大自然最根本的力量就是进化,淘汰弱者,让强者生存。
参考文章 核心链接 链接 https://www.zhihu.com/search?q=遗传算法&utm_content=search_history&type=content
参考文章附带的代码 https://github.com/yanshengjia/artificial-intelligence/blob/master/genetic-algorithm-for-functional-maximum-problem/matlab-ga

开始个人理解

首先 个人用python 重写了 matlab

目标函数

求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值

先来看看 目标函数的的校验吧 看看 在 0 - 9的区间上的值分布

测试代码

    def testTarget(self):
        plt.figure(1)
        x = np.linspace(0, 9, 10000)
        x = x.astype('float')
        y = []
        for i in range(len(x)):
            y.append(self.targetFun(x[i]))
        print(x)
        print(y)
        plt.plot(x, y)
        plt.show()

遗传算法 初步认识(一)
简介
开始个人理解
目标函数
先来看看 目标函数的的校验吧 看看 在 0 - 9的区间上的值分布
codeResult
算法流程
code
自问自答环节
下一步

result

可以看出在8附近达到最大值为25
如果我遗传算法最后的结果在这个区间内的话,那么就可以说是正确答案

codeResult

总共迭代了1000次,种群的大小是1000个基因

最佳个体 10101111011111011
最优适应度 24.855362812666666
最优个体对应自变量值 7.856726507007652
最优个体的迭代次数 11

计算耗时 81 s

算法流程

code流

gaAlgorithm = ga.GeneticAlgorithm()
# 初始化种群   随机赋值 0110值
gaAlgorithm.initPopulation()
# debug
gaAlgorithm.debug('population')
maxGN = gaAlgorithm.getMaxNumberIteration()
start = time.process_time()
for i in range(maxGN):
    # 计算适应度函数
    gaAlgorithm.fitness()
    # gaAlgorithm.debug('fitnessValue')
    gaAlgorithm.rank()
    # gaAlgorithm.debug('rank')
    gaAlgorithm.selection()
    # gaAlgorithm.debug('population')
    gaAlgorithm.crossOver()
    gaAlgorithm.mutation()
    if (i % 10 == 0):
        print(i, end=' ')
elapsed = (time.process_time() - start)
print("Time used:",elapsed)
gaAlgorithm.plotGA()
bestFitness, bestGeneration, bestIndividual = gaAlgorithm.getBestThreeAttr()

流程图

遗传算法 初步认识(一)
简介
开始个人理解
目标函数
先来看看 目标函数的的校验吧 看看 在 0 - 9的区间上的值分布
codeResult
算法流程
code
自问自答环节
下一步

每个流程详解

计算每个基因对于目标函数的适应性

    def fitness(self):
        '''适应度函数'''
        self.fitnessValue = []
        for i in range(self.populationSize):
            self.fitnessValue.append(0)
        # 先存储字符串转换出来的十进制的值, 然后带入目标函数中计算出真实的值
        for i in range(self.populationSize):
            for j in range(self.chromosomeLength):
                if (self.population[i][j] == "1"):
                    # print(self.population[i][j])
                    self.fitnessValue[i] = self.fitnessValue[i] + 2**(j)
            self.fitnessValue[i] = self.lowerBound + self.fitnessValue[i] * (self.upperBound - self.lowerBound) / (2**self.chromosomeLength - 1)
            # print('十进制值', self.fitnessValue[i])
            self.fitnessValue[i] = self.targetFun(self.fitnessValue[i]) # 因为这个直接调用了函数所以,求函数的最大值其实就是求适应值
            # print('函数值', self.fitnessValue[i])

说人话

将字符串基因转为 10进制数 然后求解目标函数 如果你求得是最大值(大部分为正数),那么适应度函数就是 转为10进制后的值 带入目标函数求得值

排序

    def rank(self):
        '''
        对个体的适应度大小进行排序,并保留最佳个体
        '''
        for i in range(self.populationSize):
            self.fitnessSum.append(0.)
        for i in range(self.populationSize):
            minIndex = i
            for j in range(i, self.populationSize):
                if (self.fitnessValue[j] < self.fitnessValue[minIndex]):
                    minIndex = j
            if (minIndex != i):
                tmp = self.fitnessValue[i]
                self.fitnessValue[i] = self.fitnessValue[minIndex]
                self.fitnessValue[minIndex] = tmp
            tmpChromosome = self.population[i]
            self.population[i] = self.population[minIndex]
            self.population[minIndex] = tmpChromosome
        for i in range(self.populationSize):
            if i == 0:
                self.fitnessSum[i] = self.fitnessSum[i] + self.fitnessValue[i]
            else:
                self.fitnessSum[i] = self.fitnessSum[i-1] + self.fitnessValue[i]
        self.fitnessAverage.append(self.fitnessSum[self.populationSize - 1] / self.populationSize)
        if(self.fitnessValue[self.populationSize - 1] > self.bestFitness):
            self.bestFitness = self.fitnessValue[self.populationSize - 1]
            self.bestGen = len(self.fitnessAverage)
            self.bestIndividual = self.population[self.populationSize - 1]

说人话

根据所有的基因的适应度,然后从小到大排序。最后一个个体是最优秀的个人(King)

选择个体

    def selection(self):
        '''
            轮盘赌选择操作,按照[0, 总适应度] 先求出结果这个是要改的 应为 这个放弃了 适应度为负的值
        '''
        self.populationNew = [None] * self.populationSize
        for i in range(self.populationSize):
            r = random.random() * self.fitnessSum[self.populationSize - 1]
            first = 0
            last = self.populationSize - 1
            mid = round((last + first) / 2)
            idx = -1
            # 原文是排序选择和 r 差不多的个体(排中法?)
            while(first <= last and idx==-1):
                if(r > self.fitnessSum[mid]):
                    first = mid
                elif( r < self.fitnessSum[mid]):
                    last = mid
                else:
                    idx = mid # 一般不会走到这个分支因为是浮点数
                    break
                mid = round((last + first) / 2)
                if(last - first == 1):
                    idx = last
                    break
            # 产生新一代的个体
            self.populationNew[i] = self.population[idx]
        if self.chooseElite:
            p = self.populationSize - 1
        else:
            p = self.populationSize
        for i in range(p):
            self.population[i] = self.populationNew[i]

说人话

有些优势基因会重复,因为放弃了(适应值为负的基因),如果开启了优势选择,那么最优秀的个体将会直接出现在新种群(待交叉配对列表) King~

交叉

    def crossOver(self):
        '''
            交叉 改进 交换前一半的基因 = 交换后一半的基因
        '''
        for i in range(0, self.populationSize, 2):
            # 生成随机概率
            if (random.random() < self.crossoverProbability):
                crossPosition = round(random.random() * self.chromosomeLength)
                if (crossPosition == 0) or (crossPosition == 1):
                    continue
                # 对crossPosition及之后的二进制串进行交换
                tmp = self.population[i][crossPosition:]
                self.population[i] = self.population[i][0:crossPosition] + self.population[i+1][crossPosition:]
                self.population[i+1] = self.population[i+1][0:crossPosition] + tmp

说人话

虽然生成了待交叉的列表,但不是每对个体都会进行交叉,如果不交叉,那么他们就会自己流传下来,如果发生了交叉,那么就和他的配对对象交换基因。

变异

    def mutation(self):
        '''
            单点变异操作
        '''
        for i in range(self.populationSize):
            if (random.random() < self.mutationProbability):
                mutationPosition = round(random.random() * self.chromosomeLength)
                if mutationPosition == self.chromosomeLength:
                    continue
                tmp = list(self.population[i])
                if ( tmp[mutationPosition] == '1'):
                    tmp[mutationPosition] = '0'
                else:
                    tmp[mutationPosition] = '1'
                self.population[i] = ''.join(tmp)

说人话

对于每一条基因,其中的某一个一个序列点 会发生翻转。

code

https://github.com/lishaohsuai/algorithm

自问自答环节

什么是 轮盘赌算法

就是每个基因的适应度值放在一个圆盘上,依据其占有的面积(适应度值)放在轮盘上,如果生成一个随机数,如果他落在这个轮盘的一个盘面上,这个小扇形代表的基因就被选中,也就是说,适应度值越高的越有可能被选中。
如果没有被选中怎么办?恭喜你可以领盒饭了,你的基因序列被自然界丢弃。

下一步

现在迭代 1000 个种群,1000次迭代花费了 81s , 下一步改写成 numpy 版本,争取 2s 内完成1000个种群的迭代,另外适应度函数有点问题,逻辑上来说,适应度函数的值不应该是负值。