遺傳算法(1) - Python實現

  • 算法特徵:
    自由空間, 定長編碼
  • 核心操做:
    選擇: 擇優選擇
    交叉: 全空間可遍歷
    變異: 加強全空間的搜索能力
  • 編碼選擇:
    二進制編碼, 字符編碼, 小數編碼
    注意: 編碼選擇以方便核心的三個操做爲準, 具體問題具體分析.
  • 適用範圍:
    通常來說, 若是一個優化問題的特徵空間知足遺傳算法的算法特徵, 那麼遺傳算法天然適用;
    若是不知足, 則問題可能須要通過必定的技巧和抽象, 使之可以進行核心的三個操做, 那麼遺傳算法仍然適用, 不然不適用. 不過此點比較依賴使用者的經驗與直覺.
  • Python代碼實現:
    說明: 本代碼採用小數編碼的方式, 求解連續空間中的多元函數.
    依賴兩個第三方庫: numpy(數值計算)與matplotlib(可視化), 沒有的須要安裝. 水平有限, 僅供參考!
     1 # 遺傳算法特徵: 自由空間, 定長編碼
     2 # 選擇: 擇優選擇
     3 # 交叉: 全空間可遍歷
     4 # 變異: 加強全空間的搜索能力
     5 
     6 import numpy  7 import matplotlib.pyplot as plt  8 
     9 # 目標函數1
     10 def myfunc1(x):  11    return (x ** 2 - 5 * x) * numpy.sin(x ** 2) * -1
     12 
     13 # 目標函數2
     14 # def myfunc2(x1, x2):
     15     # return (1 - x1) ** 2 + 100 * (x2 - x1 ** 2) ** 2
     16 
     17 
     18 # 遺傳算法: 選擇, 交叉, 變異
     19 class GA(object):  20     
     21     def __init__(self, func, lbounds, ubounds, population_size=300, maxIter=500, pm=0.01, speed=3, cf=0.1):  22         self.func = func                           # 目標函數
     23         self.lbounds = lbounds                     # 搜尋下界
     24         self.ubounds = ubounds                     # 搜尋上界
     25         self.population_size = population_size     # 種羣規模
     26         self.maxIter = maxIter                     # 最大迭代次數
     27         self.pm = pm                               # 變異機率(0, 1)
     28         self.speed=speed                           # 種羣收斂速度[1, +∞)
     29         self.cf = cf                               # 交叉因子(0, 1)
     30         
     31         self.size = len(lbounds)                   # 搜索空間的維度
     32         self.best_chrom_fitness = list()           # 最優染色體(染色體, 適應度)迭代記錄列表
     33         self.__record_fitness = list()             # 種羣適應度迭代記錄列表
     34         
     35     def solve(self):  36         # 種羣隨機初始化
     37         population = self.__init_population()  38         # 記錄種羣最優染色體信息
     39         self.__add_best(population)  40         
     41         for i in range(self.maxIter):  42             # 種羣更新
     43             population = self.__selection(population)  44             population = self.__crossover(population)  45             population = self.__mutation(population)  46             
     47             # 上一代最優個體無條件保留至下一代
     48             population[0] = self.best_chrom_fitness[-1][0]  49             # 記錄種羣最優個體
     50             self.__add_best(population)  51 
     52         self.solution = self.best_chrom_fitness[-1]  53 
     54     # 選擇: 輪盤賭方法
     55     def __selection(self, population):  56         # 適應度排序
     57         fitness = self.__cal_fitness(population)  58         new_fitness = sorted(list((ele, idx) for idx, ele in enumerate(fitness)), key=lambda item: item[0], reverse=True)  59         # 輪盤區間計算 -> 採用多項式函數對收斂速度進行調整
     60         roulette_interval = self.__cal_interval()  61         # 隨機飛鏢排序
     62         random_dart = sorted(numpy.random.random(self.population_size))  63         
     64         new_population = list()  65         idx_interval = idx_dart = 0  66         while idx_dart < self.population_size:  67             if random_dart[idx_dart] > roulette_interval[idx_interval]:  68                 idx_interval += 1
     69             else:  70                 new_population.append(population[new_fitness[idx_interval][1]])  71                 idx_dart += 1
     72         
     73         # 順序打亂
     74  numpy.random.shuffle(new_population)  75         return new_population  76     
     77     # 交叉: 對稱凸組合
     78     def __crossover(self, population):  79         # 交叉隨機數 -> 採用交叉因子提升計算精度
     80         alpha = numpy.random.random(self.population_size - 1) * self.cf  81         
     82         for idx in range(self.population_size - 1):  83             new_chrom1 = alpha[idx] * population[idx] + (1 - alpha[idx]) * population[idx + 1]  84             new_chrom2 = alpha[idx] * population[idx + 1] + (1 - alpha[idx]) * population[idx]  85             population[idx] = new_chrom1  86             population[idx + 1] = new_chrom2  87             
     88         return population  89         
     90     # 變異: 全空間變異
     91     def __mutation(self, population):  92         # 變異機率隨機數
     93         mutation_prob = numpy.random.random(self.population_size)  94         
     95         for idx, prob in enumerate(mutation_prob):  96             if prob <= self.pm:  97                 # 變異幅度隨機數
     98                 mutation_amplitude = numpy.random.uniform(-1, 1, self.size)  99                 for idx_dim, ampli in enumerate(mutation_amplitude): 100                     if ampli >= 0:    # 正向變異
    101                         population[idx][idx_dim] += ampli * (self.ubounds[idx_dim] - population[idx][idx_dim]) 102                     else:             # 負向變異
    103                         population[idx][idx_dim] += ampli * (population[idx][idx_dim] - self.lbounds[idx_dim]) 104                         
    105         return population 106    
    107     # 種羣隨機初始化
    108     def __init_population(self): 109         population = list() 110         
    111         for i in range(self.population_size): 112             chrom = list() 113             for j in range(self.size): 114  chrom.append(numpy.random.uniform(self.lbounds[j], self.ubounds[j])) 115  population.append(numpy.array(chrom)) 116         
    117         return population 118     
    119     # 種羣適應度計算
    120     def __cal_fitness(self, population): 121         fitness = list(self.func(*chrom) for chrom in population) 122         return fitness 123         
    124     # 記錄種羣最優染色體信息
    125     def __add_best(self, population): 126         fitness = self.__cal_fitness(population) 127         self.__record_fitness.append(fitness) 128         min_idx = numpy.argmin(fitness) 129  self.best_chrom_fitness.append((population[min_idx], fitness[min_idx])) 130         
    131     # 輪盤區間計算
    132     def __cal_interval(self, speed=2): 133         tmp = (numpy.arange(self.population_size) + 1) / self.population_size 134         tmp_normalize = tmp / (self.population_size + 1) * 2
    135         
    136         roulette_interval = list() 137         curr_sum = 0 138         for item in tmp_normalize: 139             curr_sum += item 140  roulette_interval.append(curr_sum) 141         
    142         roulette_interval = numpy.array(roulette_interval) ** self.speed 143         return roulette_interval 144     
    145     # 求解過程可視化展現
    146     def display(self): 147         fig = plt.figure(figsize=(8, 5)) 148         axes = plt.subplot() 149         axes.plot(self.__record_fitness, 'g.') 150         axes.plot(numpy.array(self.__record_fitness).sum(axis=1) / self.population_size, 'r-', label='$meanVal$') 151         axes.set(xlim=(-1, self.maxIter+1), xlabel='$iterCnt$', ylabel='$fitness$') 152         axes.set(title = 'solution = {}'.format(self.solution)) 153  axes.legend() 154         fig.savefig('myGA.png', dpi=500) 155  plt.show() 156  plt.close() 157 
    158 
    159 if __name__ == '__main__': 160     ins = GA(myfunc1, [-9], [5], population_size=100, maxIter=1000, pm=0.01, speed=1, cf=0.1) 161     # ins = GA(myfunc2, [-10, -10], [10, 10], population_size=100, maxIter=500, pm=0.3, speed=1, cf=0.1)
    162  ins.solve() 163     ins.display()
    View Code
  • 結果展現:
    測試函數1:
    \begin{equation}
    f(x) = (x^2 - 5x)sin(x^2) \times -1 \qquad x \in [-9, 5]
    \end{equation}
    算法

    此測試函數的主要目的在於展現遺傳算法核心的三個操做對最終結果的影響.
    測試函數2:
    \begin{equation}
    f(x_1, x_2) = (1 - x_1)^2 + 100(x_2 - {x_1}^2)^2 \qquad x_1, x_2 \in [-10, 10]
    \end{equation}

    此測試函數的主要目的在於引出高維問題. 通常對高維函數進行求解的時候常採用兩種手段:
    1. 提高種羣規模(內存開銷增大)
    2. 提升變異率(種羣不易收斂)
    建議以第二種方法爲主, 第一種方法爲輔.
  • 參考:https://blog.csdn.net/yanguilaiwuwei/article/details/46670805
相關文章
相關標籤/搜索