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

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

名詞解釋

在遺傳算法中蝌诡,染色體對應(yīng)的是數(shù)據(jù)或數(shù)組溉贿,通常是由一維的串結(jié)構(gòu)數(shù)據(jù)來表示,串上各個位置對應(yīng)基因的取值送漠⊥缯眨基因組成的串就是染色體(chromosome),或者稱為基因型個體(individual)闽寡。一定數(shù)量的個體組成了群體(population)。群體中的個體數(shù)目稱為群體大小(population size)尼酿,也成為群體規(guī)模爷狈。而各個個體對環(huán)境的適應(yīng)程度叫適應(yīng)度(fitness)。

基本步驟

編碼

GA在進(jìn)行搜索之前先將解空間的解數(shù)據(jù)表示成遺傳空間的基因型串結(jié)構(gòu)數(shù)據(jù)裳擎,這些串結(jié)構(gòu)數(shù)據(jù)的不同組合便構(gòu)成了不同的點涎永。

初始群體的生成

隨機產(chǎn)生N個初始串結(jié)構(gòu)數(shù)據(jù),每個串結(jié)構(gòu)數(shù)據(jù)稱為一個個體鹿响,N個個體構(gòu)成了一個群體羡微。GA以這N個串結(jié)構(gòu)數(shù)據(jù)作為初始點開始進(jìn)化。

適應(yīng)度評估

適應(yīng)度表明個體或解的優(yōu)劣性惶我。不同的問題妈倔,適應(yīng)度函數(shù)的定義方式也不同。

選擇

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

交叉

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

變異

變異首先在群體中隨機選擇一個個體声搁,對于選中的個體以一定的概率隨機地改變串結(jié)構(gòu)數(shù)據(jù)中某個串的的值黑竞。同生物界一樣,GA中變異發(fā)生的概率很低酥艳,通常取值很小摊溶。

實例詳解

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

目標(biāo)函數(shù)

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)

二進(jìn)制編碼

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

二進(jìn)制解碼

解碼即編碼的逆過程:
$${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)

種群初始化

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

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))

選擇

選擇我們采用輪盤賭方式進(jìn)行選擇研儒,主要思想是適應(yīng)度高的,被選擇到的概率大独令。


沒怎么優(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

變異

這里的變異,我們先以變異概率碗硬,從種群中選一個,然后對選中的個體瓢颅,隨機選一個變異位點進(jìn)行變異恩尾。

    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

算法主流程

至此冀偶,遺傳的主要框架已經(jīng)完畢,下面展示主流程渔嚷,及畫圖部分代碼进鸠。

    def ga(self):
        """
        算法主函數(shù)
        :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()

實驗結(jié)果圖

總結(jié)

在編碼的時候,我偷懶了一下形病,把兩個變量拆開寫客年,x1和x2霞幅,導(dǎo)致之后的操作變得異常復(fù)雜,并且不利于代碼重構(gòu)量瓜。
程序中過多的使用了for循環(huán)司恳,并沒有對此進(jìn)行優(yōu)化。
針對上述兩個問題绍傲,在此記錄一下扔傅。

程序完整代碼

Genetic-Algorithms

參考資料

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烫饼,隨后出現(xiàn)的幾起案子猎塞,更是在濱河造成了極大的恐慌,老刑警劉巖杠纵,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邢享,死亡現(xiàn)場離奇詭異,居然都是意外死亡淡诗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門伊履,熙熙樓的掌柜王于貴愁眉苦臉地迎上來韩容,“玉大人,你說我怎么就攤上這事唐瀑∪盒祝” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵哄辣,是天一觀的道長请梢。 經(jīng)常有香客問我,道長力穗,這世上最難降的妖魔是什么毅弧? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮当窗,結(jié)果婚禮上够坐,老公的妹妹穿的比我還像新娘。我一直安慰自己崖面,他們只是感情好元咙,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巫员,像睡著了一般庶香。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上简识,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天赶掖,我揣著相機與錄音感猛,去河邊找鬼。 笑死倘零,一個胖子當(dāng)著我的面吹牛唱遭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呈驶,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拷泽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了袖瞻?” 一聲冷哼從身側(cè)響起司致,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聋迎,沒想到半個月后脂矫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡霉晕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年庭再,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牺堰。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拄轻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伟葫,到底是詐尸還是另有隱情恨搓,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布筏养,位于F島的核電站斧抱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渐溶。R本人自食惡果不足惜辉浦,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掌猛。 院中可真熱鬧盏浙,春花似錦、人聲如沸荔茬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慕蔚。三九已至丐黄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孔飒,已是汗流浹背灌闺。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工艰争, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桂对。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓甩卓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蕉斜。 傳聞我的和親對象是個殘疾皇子逾柿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容

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