遺傳算法(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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM