- 算法特徵:
自由空間, 定長編碼 - 核心操做:
選擇: 擇優選擇
交叉: 全空間可遍歷
變異: 加強全空間的搜索能力 - 編碼選擇:
二進制編碼, 字符編碼, 小數編碼
注意: 編碼選擇以方便核心的三個操做爲準, 具體問題具體分析. - 適用範圍:
通常來說, 若是一個優化問題的特徵空間知足遺傳算法的算法特徵, 那麼遺傳算法天然適用;
若是不知足, 則問題可能須要通過必定的技巧和抽象, 使之可以進行核心的三個操做, 那麼遺傳算法仍然適用, 不然不適用. 不過此點比較依賴使用者的經驗與直覺. - 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()
-
結果展現:
此測試函數的主要目的在於展現遺傳算法核心的三個操做對最終結果的影響.
測試函數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