基因演算法解决函数极值问题-python版

基因演算法解决函数极值问题-python版

算法 ,基因演算法,python,numpy


1.求二元函数在以下区间的近似最大值

f(x , y) = 21.5 + sin(4 * PI * x) + sin(20 * PI * y)
(3.0<=x<=12.1,4.1<=y<=5.8)

2.直接上代码

import numpy as np
import random
from math import ceil, log, pow, pi, sin

class GeneticAlogorithm:
    #约束变量
    a1, b1, a2, b2=3.0,12.1,4.1,5.8
    #存储相关值
    precison=10000
    output=[0.0,0.0,0.0]

    def init(self,M,T,Pc,Pm):
        self.M,self.T,self.Pc,self.Pm=M,T,Pc,Pm
        #码位,二进制编码长度
        self.xLen=int(ceil(log((self.b1-self.a1)*self.precison,2)))
        self.yLen=int(ceil(log((self.b2-self.a2)*self.precison,2)))
        self.Len=self.xLen+self.yLen
        #存储种群基因
        self.population=np.random.random(0, 2, size=(self.M, self.Len))
        self.value=np.zeros(self.M)

    def decodegroup(self):
        x=y=0.0
        tmp_value=[]
        for i in range(self.M):
            tmp=0.0
            for j in range(0, self.xLen):
                tmp=tmp+self.population[i][j] * pow(2, j) 
            x = tmp * (self.b1- self.a1) / (pow(2, self.xLen) - 1) + self.a1 
            #
            tmp=0.0
            for j in range(self.xLen, self.Len):
                tmp=tmp+self.population[i][j] * pow(2, j-self.xLen) 
            y = tmp * (self.b2- self.a2) / (pow(2, self.yLen) - 1) + self.a2
            cal_value=21.5 + x * sin(4 * pi * x ) + y * sin(20 * pi * y)
            tmp_value.append(cal_value)
            if cal_value > self.output[2]:
                self.output=[x, y, cal_value]
            ###
            self.value=np.array(tmp_value)

    def makecopy(self):
        #计算所有的值的和
        value_sum = np.sum(self.value)
        #计算适应度
        self.value = np.add.accumlate(self.value)
        value_pi = self.value / value_sum
        value_pi = np.add.acculate(value_pi)
        #轮盘赌法
        choice=np.zeros(self.M)
        for i in range(self.M):
            for j in range(self.M):
                if random.random()  < value_pi[j]:
                    choice[i]=j
                    break
        self.population=self.population[choice]

    def doswitch(self):
        '''
        单点交叉算子 交叉概率 Pc'''
        tmp = np.random.permutation(self.M)
        self.population[tmp]
        for i in range(0, self.M, 2):
            index=int(random.randint(1,self.M-1) * self.Pc)
            self.population[i][-index:],self.population[i+1][-index:]=self.population[i+1][-index:],self.population[i][-index:]


    def domutation(self):
        '''
        平均每代会有cnt个基因发生突变'''
        count=int(ceil(self.Pm * self.M * self.Len))
        for i in range(count):
            index=random.randint(0, self.M * self.Len-1)#[a,b]
            m,n=index/self.Len, index%self.Len
            self.population[m][n]= 1 if self.population[m][n]==1 else 0

    def run(self):
        self.init(1000, 1700, 0.1345, 0.0001234)
        self.decodegroup()
        for i in range(self.T):
            self.makecopy
            self.doswitch()
            self.domutation()
            self.decodegroup()
        print self.output

if __name__=='__main__':
    hao=GeneticAlogorithm()
    hao.run()

3.代码解释

3.1导入相关库

import numpy as np
import random
from math import ceil, log, pow, pi, sin
3.2相关约束变量
 #约束变量
 a1, b1, a2, b2=3.0,12.1,4.1,5.8
 #保留精度的位数,最后其实没用,还是默认系统的位数,如果真的保留五位数,最后最大值没有区分度
 precison=10000
  #存储近似最大值,以及近似最大值时的x和y
 output=[0.0,0.0,0.0]
3.3相关初始化
 def init(self,M,T,Pc,Pm):
        self.M,self.T,self.Pc,self.Pm=M,T,Pc,Pm
        #码位,二进制编码长度
        self.xLen=int(ceil(log((self.b1-self.a1)*self.precison,2)))
        self.yLen=int(ceil(log((self.b2-self.a2)*self.precison,2)))
        self.Len=self.xLen+self.yLen
        #存储种群基因
        self.population=np.random.random(0, 2, size=(self.M, self.Len))
        self.value=np.zeros(self.M)
  • self.M:种群初始大小(数量)
  • self.T:种群演化的代数
  • self.Pc:种群基因交差概率
  • self.Pm:种群基因突变概率
  • self.xLen:代表X的基因长度
  • self.yLen:代表Y的基因长度
  • self.Len:种群一个个体的基因长度,码长
  • self.population:存储一个种群的全部基因,就是01序列
  • self.value:存储一个种群的表现,怎么说,放在函数就是函数值,至于为什么要把一代群体所有的值都保存下来,直接保留一个最大值不可以吗?不可以,需要根据这一代种群所有的value来计算每个个体的适应度

至于xLen,yLen:

2^ m <= (b-a) * presion <= 2^(m+1)

解释一下这个函数:

np.random.random(0, 2, size=(self.M, self.Len))

该函数返回一个self.M * self.Len的二维数组,由随机的0,1填充

type(walks)
type ‘numpy.ndarray’

3.4解码并更新近似最大值
  def decodegroup(self):
       x=y=0.0
       tmp_value=[]
       for i in range(self.M):
           tmp=0.0
           for j in range(0, self.xLen):
               tmp=tmp+self.population[i][j] * pow(2, j) 
           x = tmp * (self.b1- self.a1) / (pow(2, self.xLen) - 1) + self.a1 
           #
           tmp=0.0
           for j in range(self.xLen, self.Len):
               tmp=tmp+self.population[i][j] * pow(2, j-self.xLen) 
           y = tmp * (self.b2- self.a2) / (pow(2, self.yLen) - 1) + self.a2
           cal_value=21.5 + x * sin(4 * pi * x ) + y * sin(20 * pi * y)
           tmp_value.append(cal_value)
           if cal_value > self.output[2]:
               self.output=[x, y, cal_value]
           ###
           self.value=np.array(tmp_value)

二进制转十进制这就不用说了,说说如何转换成区间的值:

x = tmp * (self.b1- self.a1) / (pow(2, self.xLen) - 1) + self.a1
基础值+占的份数*每一份的占的值=在这个区间的实际值

3.5选择运算
def makecopy(self):
     #计算所有的值的和
     value_sum = np.sum(self.value)
     #计算适应度
     self.value = np.add.accumlate(self.value)
     value_pi = self.value / value_sum
     value_pi = np.add.acculate(value_pi)
     #轮盘赌法
     choice=np.zeros(self.M)
     for i in range(self.M):
         for j in range(self.M):
             if random.random()  < value_pi[j]:
                 choice[i]=j
                 break
     self.population=self.population[choice]

这里写图片描述
这里写图片描述

如何选择见上面图片。采自这里

3.6交差运算
 def doswitch(self):
     '''
     单点交叉算子 交叉概率 Pc'''
     tmp = np.random.permutation(self.M)
     self.population[tmp]
     for i in range(0, self.M, 2):
         index=int(random.randint(1,self.M-1) * self.Pc)
         self.population[i][-index:],self.population[i+1][-index:]=self.population[i+1][-index:],self.population[i][-index:]
  • 不清楚np.random.permutation(self.M)的意义
>>> tmp = np.random.permutation(15)
>>> tmp
array([ 0, 10, 13, 12,  5,  2,  6,  9,  3,  8,  4, 14,  1, 11,  7])
  • 二维数组self.population根据tmp切片,打比方说,就是现在第2列装的是原来的第10列
   tmp = np.random.permutation(self.M)
   self.population[tmp]

3.7变异运算

  def domutation(self):
      '''
      平均每代会有cnt个基因发生突变'''
      count=int(ceil(self.Pm * self.M * self.Len))
      for i in range(count):
          index=random.randint(0, self.M * self.Len-1)#[a,b]
          m,n=index/self.Len, index%self.Len
          self.population[m][n]= 1 if self.population[m][n]==1 else 0
  • 根据种群的总基因数种群突变概率算出平均每一代种群的突变基因个数count
  • 随机count次,让某一个体的某一位基因突变,0->1,1->0
3.8程序运行流程图
   def run(self):
        self.init(1000, 1700, 0.1345, 0.0001234)
        self.decodegroup()
        for i in range(self.T):
            self.makecopy
            self.doswitch()
            self.domutation()
            self.decodegroup()
        print self.output
Created with Raphaël 2.1.0 开始 种群P(t) 个体适应度评价 选择运算,交差运算,变异运算 种群P(t+1) 解码并更新近似最大值 达到设定种群演化代数? 输出近似最大值,以及近似最大值时的x和y 结束 yes no

注:程序效率不高,并没有很接近实际最大值,不过先这样吧,,也没想明白道理,毕竟python也刚入门,之所以用python写,一是:为了练习python的numpy库,二是:这个库以及python的切片索引功能来解释这个基因演算法条理清楚,代码简洁。之后会有C#版,效率更高,敬请期待。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值