【計算智能】目標優(yōu)化智能算法之遺傳算法(Python實現(xiàn))

遺傳算法(genetic algorithm, GA)是一種進化算法,其基本原理是仿效生物界中的“物競天擇,適者生存”的演化法則。遺傳算法是把問題參數編碼為染色體,再利用迭代的方式進行選擇、交叉以及變異等運算來交換種群中染色體的信息,最終生成符合優(yōu)化目標的染色體。
更好閱讀體驗請訪問(https://tianle.me/2017/04/19/GA/

名詞解釋

在遺傳算法中,染色體對應的是數據或數組,通常是由一維的串結構數據來表示,串上各個位置對應基因的取值。基因組成的串就是染色體(chromosome),或者稱為基因型個體(individual)。一定數量的個體組成了群體(population)。群體中的個體數目稱為群體大小(population size),也成為群體規(guī)模。而各個個體對環(huán)境的適應程度叫適應度(fitness)。

基本步驟

編碼

GA在進行搜索之前先將解空間的解數據表示成遺傳空間的基因型串結構數據,這些串結構數據的不同組合便構成了不同的點。

初始群體的生成

隨機產生N個初始串結構數據,每個串結構數據稱為一個個體,N個個體構成了一個群體。GA以這N個串結構數據作為初始點開始進化。

適應度評估

適應度表明個體或解的優(yōu)劣性。不同的問題,適應度函數的定義方式也不同。

選擇

選擇的目的是為了從當前群體中選出優(yōu)良的個體,使它們有機會作為父代為下一代繁殖子孫。遺傳算法通過選擇過程體現(xiàn)這一思想,進行選擇的原則是適應度強的個體為下一代貢獻一個或多個后代的概率大。選擇體現(xiàn)了達爾文的適者生存原則。

交叉

交叉操作是遺傳算法中最主要的遺傳操作。通過交叉操作可以得到新一代個體,新個體組合了其父輩個體的特性。交叉體現(xiàn)了信息交換的思想。

變異

變異首先在群體中隨機選擇一個個體,對于選中的個體以一定的概率隨機地改變串結構數據中某個串的的值。同生物界一樣,GA中變異發(fā)生的概率很低,通常取值很小。

實例詳解

之前已經使用matlab實現(xiàn)了一次,由于現(xiàn)在又布置了作業(yè),正好現(xiàn)在對python不是特別熟悉,那就寫個代碼練練手吧。

目標函數

max    f (x1, x2) = 21.5 + x1·sin(4 pi x1) + x2·sin(20 pi x2)  
  
s. t.    -3.0 <= x1 <= 12.1  
          4.1 <= x2 <= 5.8  
def func(self):
        self.decoding(self.code_x1, self.code_x2)
        self.y = 21.5 + self.x1 * math.sin(4 * math.pi * self.x1) + self.x2 * math.sin(20 * math.pi * self.x2)

二進制編碼

在剛剛提到的遺傳算法中,我們首先要將數據進行編碼,這里我們采用二進制的方式進行編碼。第一步,我們根據題目的介紹可以得知該函數含有兩個變量,以及各自的定義域。在二進制編碼中,我們首先要先計算它的編碼長度。計算公式如下:
$${2^{{m_j} - 1}} < ({b_j} - {a_j})*precision \le {2^{{m_j}}} - 1$$
其中precision為精度,如小數點后5位,則precision=10^5,mj為編碼長度,${x_j} \in [{a_j},{b_j}]$

二進制解碼

解碼即編碼的逆過程:
$${x_j} = {a_j} + {\rm{decimal}}(substrin{g_j}) \times \frac{{{b_j} - {a_j}}}{{{2^{{m_j}}} - 1}}$$

def decoding(self, code_x1, code_x2):
        self.x1 = self.bounds[0][0] + int(code_x1, 2) * (self.bounds[0][1] - self.bounds[0][0]) / (
        2 ** self.code_x1_length - 1)
        self.x2 = self.bounds[1][0] + int(code_x2, 2) * (self.bounds[1][1] - self.bounds[1][0]) / (
        2 ** self.code_x2_length - 1)

種群初始化

編碼完成那我們就開始對種群初始化吧,為了簡便我采用了隨機地方式進行初始化。

def __init__(self, bounds, precision):
        self.x1 = 1
        self.x2 = 1

        self.y = 0

        self.code_x1 = ''
        self.code_x2 = ''

        self.bounds = bounds

        temp1 = (bounds[0][1] - bounds[0][0]) * precision
        self.code_x1_length = math.ceil(math.log(temp1, 2))

        temp2 = (bounds[1][1] - bounds[1][0]) * precision
        self.code_x2_length = math.ceil(math.log(temp2, 2))

        self.rand_init()
        self.func()
        
def rand_init(self):
        for i in range(self.code_x1_length):
            self.code_x1 += str(random.randint(0, 1))

        for i in range(self.code_x2_length):
            self.code_x2 += str(random.randint(0, 1))

選擇

選擇我們采用輪盤賭方式進行選擇,主要思想是適應度高的,被選擇到的概率大。


沒怎么優(yōu)化,用了一堆for循環(huán)。。。。

    def select(self):
        """
        輪盤賭選擇
        :return:
        """
        # calculate fitness function
        sum_f = 0
        for i in range(self.pop_size):
            self.pop[i].func()

        # guarantee fitness > 0
        min = self.pop[0].y
        for i in range(self.pop_size):
            if self.pop[i].y < min:
                min = self.pop[i].y
        if min < 0:
            for i in range(self.pop_size):
                self.pop[i].y = self.pop[i].y + (-1) * min

        # roulette
        for i in range(self.pop_size):
            sum_f += self.pop[i].y
        p = [0] * self.pop_size
        for i in range(self.pop_size):
            p[i] = self.pop[i].y / sum_f
        q = [0] * self.pop_size
        q[0] = 0
        for i in range(self.pop_size):
            s = 0
            for j in range(0, i+1):
                s += p[j]
            q[i] = s
        # start roulette
        v = []
        for i in range(self.pop_size):
            r = random.random()
            if r < q[0]:
                v.append(self.pop[0])
            for j in range(1, self.pop_size):
                if q[j - 1] < r <= q[j]:
                    v.append(self.pop[j])
        self.pop = v

變異

這里的變異,我們先以變異概率,從種群中選一個,然后對選中的個體,隨機選一個變異位點進行變異。

    def mutation(self):
        """
        變異
        :return:
        """
        for i in range(self.pop_size):
            if self.pm > random.random():
                pop = self.pop[i]
                # select mutation index
                index1 = random.randint(0, pop.code_x1_length-1)
                index2 = random.randint(0, pop.code_x2_length-1)

                i = pop.code_x1[index1]
                i = self.__inverse(i)
                pop.code_x1 = pop.code_x1[:index1] + i + pop.code_x1[index1+1:]

                i = pop.code_x2[index2]
                i = self.__inverse(i)
                pop.code_x2 = pop.code_x2[:index2] + i + pop.code_x2[index2+1:]

交叉

這里采用單點交叉法。隨機從種群中選兩個個體,然后再隨機選一個交叉點,交換位置。看圖 = . =

    def cross(self):
        """
        交叉
        :return:
        """
        for i in range(int(self.pop_size / 2)):
            if self.pc > random.random():
                # randon select 2 chromosomes in pops
                i = 0
                j = 0
                while i == j:
                    i = random.randint(0, self.pop_size-1)
                    j = random.randint(0, self.pop_size-1)
                pop_i = self.pop[i]
                pop_j = self.pop[j]

                # select cross index
                pop_1 = random.randint(0, pop_i.code_x1_length - 1)
                pop_2 = random.randint(0, pop_i.code_x2_length - 1)

                # get new code
                new_pop_i_code1 = pop_i.code_x1[0: pop_1] + pop_j.code_x1[pop_1: pop_i.code_x1_length]
                new_pop_i_code2 = pop_i.code_x2[0: pop_2] + pop_j.code_x2[pop_2: pop_i.code_x2_length]

                new_pop_j_code1 = pop_j.code_x1[0: pop_1] + pop_i.code_x1[pop_1: pop_i.code_x1_length]
                new_pop_j_code2 = pop_j.code_x2[0: pop_2] + pop_i.code_x2[pop_2: pop_i.code_x2_length]

                pop_i.code_x1 = new_pop_i_code1
                pop_i.code_x2 = new_pop_i_code2

                pop_j.code_x1 = new_pop_j_code1
                pop_j.code_x2 = new_pop_j_code2

算法主流程

至此,遺傳的主要框架已經完畢,下面展示主流程,及畫圖部分代碼。

    def ga(self):
        """
        算法主函數
        :return:
        """
        self.init_pop()
        best = self.find_best()
        self.g_best = copy.deepcopy(best)
        y = [0] * self.pop_size
        for i in range(self.max_gen):
            self.cross()
            self.mutation()
            self.select()
            best = self.find_best()
            self.bests[i] = best
            if self.g_best.y < best.y:
                self.g_best = copy.deepcopy(best)
            y[i] = self.g_best.y
            print(self.g_best.y)

        # plt
        plt.figure(1)
        x = range(self.pop_size)
        plt.plot(x, y)
        plt.ylabel('generations')
        plt.xlabel('function value')
        plt.show()

實驗結果圖

總結

在編碼的時候,我偷懶了一下,把兩個變量拆開寫,x1和x2,導致之后的操作變得異常復雜,并且不利于代碼重構。
程序中過多的使用了for循環(huán),并沒有對此進行優(yōu)化。
針對上述兩個問題,在此記錄一下。

程序完整代碼

Genetic-Algorithms

參考資料

《MATLAB智能算法-30個案例分析》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

  • Funda T, El-Kassaby YA (2012) Seed orchard genetics. CAB ...
    董八七閱讀 900評論 0 2
  • 本文最早發(fā)表在本人博客:http://www.gotoli.us/?p=1518 我們接著上面的例子。上面例子是一...
    algorithmdog閱讀 7,338評論 2 13
  • 姓名:張藝倫 學號:17011210282 轉載自:https://www.zhihu.com/question...
    DZNGGZGY閱讀 1,308評論 0 0
  • 今天的命題本來是對2016年的總結。但在總結的過程中,我想起一件一直想做的事。于是我就跑題了。這件事就是整理我的財...
    黎思岐閱讀 554評論 2 4
  • 這個話題來自夫妻倆的辯論······· 話題的緣由來自一張圖片,一個非洲部落國家參加國際會議時的著裝。在周圍一片西...
    老其實話閱讀 239評論 0 0