遗传算法(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()
-
结果展示:
此测试函数的主要目的在于展示遗传算法核心的三个操作对最终结果的影响.
测试函数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