遺傳算法的Python實現(通過遺傳算法實現函數擬合)

遺傳算法

簡介

  遺傳算法是允許高度並行的算法,工程師通常使用Cuda實現遺傳算法以應用到工程實際中,筆者在這裏以Python語法實現,旨在能讓更多的人理解遺傳算法。

問題描述

  利用遺傳算法求解如下最優化問題
  min  F=i=13Wig(Qi)min \ \ F = \sum_{i=1}^{3}W_{i}g(Q_{i})
  其中 g(x)=0.1544×x2+1.181×x+0.6028g(x)=-0.1544\times x^{2}+1.181\times x+0.6028
  限制條件
  Qe=i=13WiQiQ_{e}=\sum_{i=1}^{3}W_{i}Q_{i}
  1.5Qi3.341.5\leq Q_{i} \leq 3.34
  Wi=01W_{i} = 0或1
  實現遺傳算法主要有這樣幾步,第一步,編碼

編碼

  爲了滿足精度的同時避免使用小數點,我們需要將自變量QiQ_{i}乘以1000010000;當然也可以採用其它設計方法,這裏考慮到編程工具和計算機算力,設爲此值。
  Qi:(3.341.5)×10000=18400Q_{i}:(3.34-1.5)\times 10000 = 18400
  214184002152^{14}\leq 18400 \leq 2^{15}
  故對QiQ_{i}採用編碼長度爲15,對WiW_{i}採用編碼長度爲1

算法流程

  1.種羣初始化
  2.選擇
  3.繁殖
  4.變異
  5.是否滿足循環次數:如果滿足,則退出,否則,回到2

種羣初始化

  首先需要對種羣進行初始化

    #種羣初始化
    def groupinit(self):
        for i in range(self.group_size):
            self.group.append(individual())
        for i in range(self.group_size):
            #總基因長度:15*2+1*2,即基因的前30位代碼Q1,Q2...
            for j in range(32):
                self.group[i].gene[j] = random.randint(0,1)
        self.showgene()

將基因翻譯成係數

    #基因展示(將具有相應基因的個體,將基因性質表達出來)
    def showgene(self):
        for i in range(self.group_size):
            temp = 0
            for j in range(15):
                temp|=self.group[i].gene[j]*2**j
            self.group[i].Q[0] = 1.5+ temp*(3.34-1.5)/(pow(2.0,15)-1)
            temp = 0
            for j in range(15):
                temp|=self.group[i].gene[j+15]*2**j
            self.group[i].Q[1] = 1.5+ temp*(3.34-1.5)/(pow(2.0,15)-1)
            #print(self.group[i].Q[0],self.group[i].Q[1])
            self.group[i].W[0]=self.group[i].gene[30]
            self.group[i].W[1]=self.group[i].gene[31]
            _ = self.Qe -self.group[i].W[0]*self.group[i].Q[0]-self.group[i].W[1]*self.group[i].Q[1]
            if _<1.5:
                self.group[i].flag = False
                self.group[i].fitness=0.0
            elif _ >3.34:
                self.group[i].flag = False
                self.group[i].W[2] = 0
                self.group[i].Q[2] = 0
                self.group[i].fitness=0.0
            else:
                self.group[i].flag = True
                self.group[i].W[2] = 1
                self.group[i].Q[2] = _
                _output = 0.0
                for k in range(3):
                   _output+=self.group[i].W[k]*((-0.1544)*self.group[i].Q[k]*self.group[i].Q[k]+1.181*self.group[i].Q[k]+0.6028)
                self.group[i].fitness =np.exp( -_output)

選擇

  傳統遺傳算法的選擇方式採用輪盤賭的方式將表現差的對象進行淘汰;但這裏要解決問題的方式與傳統的遺傳算法解決的問題不一致。如果採用輪盤賭的方式,一些明明很接近有效值卻因爲不滿足限制條件的個體則會被淘汰。這裏仿照人類社會的方式進行選擇。表現分最好的爲國王,國王擁有絕對的存活率,表現分佔前百分之十(不包括國王)的存活率是百分之九十,其它個體成爲平民,平民擁有百分之七十的存活率。

 #自然選擇
    def choose(self):

        #排序,找到國王與貴族(設置第一位國王,貴族,貧民,國王不會被選擇淘汰,貴族百分之90的生存率,貧民百分之60)
        _noble = int(self.group_size/10)
        #存在限制條件,更類似於人類社會
        cmpfun = operator.attrgetter('fitness')#參數爲排序依據的屬性,可以有多個,這裏優先id,使用時按需求改換參數即可
        self.group.sort(key =cmpfun,reverse = True)

        #for i in range(self.group_size):
        #    print(self.group[i].fitness)

        new_group = []
        new_group.append(self.group[0])
       
        #貴族
        for i in range(1,_noble):
            _rand = random.random()
            if(0.9>_rand):
                new_group.append(self.group[i])
        #平民
        for i in range(_noble,self.group_size):
            _rand = random.random()
            if(0.6>_rand):
                new_group.append(self.group[i])
        self.group = new_group

繁衍(基因交換)

  我們設置了種羣規模,在選擇之後,部分個體被淘汰,這裏通過繁衍的方式讓種羣規模恢復。先隨機選擇父本和木本,然後選取交換基因的結點

    #繁衍(通過基因轉換,構造新的種羣)
    def Propagate(self):
        #繁衍第一步,隨機選擇父本和母本
        _len_group = len(self.group)
        _num = self.group_size - _len_group 
        for _i in range(_num):
            #隨機選擇父本和母本
            _father  = random.randint(0,_len_group-1)
            _mother = random.randint(0,_len_group-1)
            _gp = random.randint(0,31)#選擇基因交換位置
            _ = individual()
            _.gene = self.group[_father].gene[0:_gp]+self.group[_mother].gene[_gp:32]
            self.group.append(_)

變異

  變異是遺傳算法最爲核心的內容,通過變異,引入了更多的基因型。

    #進行變異
    def mutation(self):
        #國王不發生變異,其餘個體按照變異率進行變異
        for _i in range(1,self.group_size):
            _rand = random.random()
            if(self.mutationprop>_rand):
                _gp = random.randint(0,31)#選擇基因變異的位置
                if(self.group[_i].gene[_gp]==1):
                    self.group[_i].gene[_gp] = 0
                else:
                    self.group[_i].gene[_gp] = 1

對原問題的思考

在這裏插入圖片描述

'''
file: plugin.py
本文根據作者的想法構建了一個特殊的遺傳類
除模擬遺傳算法外,還模擬了簡單的人類社會
'''
import numpy as np
import random
import operator

def g(x):
    return np.float(-0.1544*x*x+1.181*x+0.6028)



class individual:
    def __init__(self):
        self.W = np.array([0.0,0.0,0.0],np.float)
        self.Q = np.array([0.0,0.0,0.0],np.float)
        self.gene =[0]*32
        self.falg = False
        self.fitness =0.0
        

class GA:
    '''
    para:
    group_size 種羣數量
    Qe 限制條件中Qe的值
    '''
    def __init__(self,group_size,Qe,mutationprop):
        self.group_size = group_size
        self.group =[]
        self.Qe = Qe
        self.mutationprop = mutationprop
        self.groupinit()
        
    #種羣初始化
    def groupinit(self):
        for i in range(self.group_size):
            self.group.append(individual())
        for i in range(self.group_size):
            #總基因長度:15*2+1*2,即基因的前30位代碼Q1,Q2...
            for j in range(32):
                self.group[i].gene[j] = random.randint(0,1)
        self.showgene()
    #基因展示(將具有相應基因的個體,將基因性質表達出來)
    def showgene(self):
        for i in range(self.group_size):
            temp = 0
            for j in range(15):
                temp|=self.group[i].gene[j]*2**j
            self.group[i].Q[0] = 1.5+ temp*(3.34-1.5)/(pow(2.0,15)-1)
            temp = 0
            for j in range(15):
                temp|=self.group[i].gene[j+15]*2**j
            self.group[i].Q[1] = 1.5+ temp*(3.34-1.5)/(pow(2.0,15)-1)
            #print(self.group[i].Q[0],self.group[i].Q[1])
            self.group[i].W[0]=self.group[i].gene[30]
            self.group[i].W[1]=self.group[i].gene[31]
            _ = self.Qe -self.group[i].W[0]*self.group[i].Q[0]-self.group[i].W[1]*self.group[i].Q[1]
            if _<1.5:
                self.group[i].flag = False
                self.group[i].fitness=0.0
            elif _ >3.34:
                self.group[i].flag = False
                self.group[i].W[2] = 0
                self.group[i].Q[2] = 0
                self.group[i].fitness=0.0
            else:
                self.group[i].flag = True
                self.group[i].W[2] = 1
                self.group[i].Q[2] = _
                _output = 0.0
                for k in range(3):
                   _output+=self.group[i].W[k]*((-0.1544)*self.group[i].Q[k]*self.group[i].Q[k]+1.181*self.group[i].Q[k]+0.6028)
                self.group[i].fitness =np.exp( -_output)



    #自然選擇
    def choose(self):

        #排序,找到國王與貴族(設置第一位國王,貴族,貧民,國王不會被選擇淘汰,貴族百分之90的生存率,貧民百分之60)
        _noble = int(self.group_size/10)
        #存在限制條件,更類似於人類社會
        cmpfun = operator.attrgetter('fitness')#參數爲排序依據的屬性,可以有多個,這裏優先id,使用時按需求改換參數即可
        self.group.sort(key =cmpfun,reverse = True)

        #for i in range(self.group_size):
        #    print(self.group[i].fitness)

        new_group = []
        new_group.append(self.group[0])
       
        #貴族
        for i in range(1,_noble):
            _rand = random.random()
            if(0.9>_rand):
                new_group.append(self.group[i])
        #平民
        for i in range(_noble,self.group_size):
            _rand = random.random()
            if(0.6>_rand):
                new_group.append(self.group[i])
        self.group = new_group
    #繁衍(通過基因轉換,構造新的種羣)
    def Propagate(self):
        #繁衍第一步,隨機選擇父本和母本
        _len_group = len(self.group)
        _num = self.group_size - _len_group 
        for _i in range(_num):
            #隨機選擇父本和母本
            _father  = random.randint(0,_len_group-1)
            _mother = random.randint(0,_len_group-1)
            _gp = random.randint(0,31)#選擇基因交換位置
            _ = individual()
            _.gene = self.group[_father].gene[0:_gp]+self.group[_mother].gene[_gp:32]
            self.group.append(_)
    
    #進行變異
    def mutation(self):
        #國王不發生變異,其餘個體按照變異率進行變異
        for _i in range(1,self.group_size):
            _rand = random.random()
            if(self.mutationprop>_rand):
                _gp = random.randint(0,31)#選擇基因變異的位置
                if(self.group[_i].gene[_gp]==1):
                    self.group[_i].gene[_gp] = 0
                else:
                    self.group[_i].gene[_gp] = 1

        #打印國王信息
    def showtheking(self):
        print("國王的信息")
        print("適應度",self.group[0].fitness)
        print("係數W",self.group[0].W[0],self.group[0].W[1],self.group[0].W[2])
        print("係數Q",self.group[0].Q[0],self.group[0].Q[1],self.group[0].Q[2])
        print("函數的最優解",-np.log(self.group[0].fitness))
    def showgroup(self):
        for i in range(self.group_size):
            print("適應度",self.group[i].fitness)
            print("係數W",self.group[i].W[0],self.group[i].W[1],self.group[i].W[2])
            print("係數Q",self.group[i].Q[0],self.group[i].Q[1],self.group[i].Q[2])
            print("函數的值",-np.log(self.group[i].fitness))
    def run(self,_cyclenum):
        for _i in range(_cyclenum):
            self.choose()
            self.Propagate()
            self.mutation()
            self.showgene()
            #self.showtheking()
        self.showgroup()
        self.showtheking()
#file main.py

from plugin import GA


if __name__ == "__main__":

    _ga =GA(4000,3.62,0.2)
    _ga.run(400)

Python推廣

  使用Python或者OCtave,matlab等編程語言進行開發是十分有必要的,先用便捷的編程語言驗證算法的可行性,再用C++/Java去實現算法,這一點對算法工程師來說,尤爲重要。讀者可以加入QQ羣,交流學習Python
916372346。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章