遗传算法(Genetic Algorithm,GA)是一种受自然选择和遗传学启发的优化算法。在计算机科学和人工智能领域,遗传算法被广泛用于解决复杂的优化和搜索问题。本文将介绍遗传算法的基本原理、工作流程及其应用。
遗传算法的基本原理
遗传算法的灵感来源于生物进化过程中的自然选择和遗传变异。基本思想是模拟生物进化过程,通过选择、交叉和变异操作,使得种群中的个体逐步优化,最终找到问题的最优解。
主要概念
- 染色体编码 :染色体的编码结果是一个二进制符号序列,便于后面交叉和变异
- 种群(Population) :一组可能的解(个体),每个个体表示问题的一个解。
- 个体(Individual) :种群中的一个成员,通常用一个字符串或数组表示。
- 基因(Gene) :个体的组成部分,表示解的一个特定特征。
- 适应度函数(Fitness Function) :评估个体优劣的函数,适应度值越高表示个体越优越。
- 选择(Selection) :根据适应度值选择个体进行繁殖,适应度高的个体更有可能被选择。
- 交叉(Crossover) :两个个体交换部分基因,生成新的个体。
- 变异(Mutation) :随机改变个体的一部分基因,增加种群的多样性。
遗传算法的工作流程
遗传算法的工作流程可以概括为以下几个步骤:
- 初始化种群:随机生成初始种群,包含若干个体。
- 评估适应度:计算每个个体的适应度值。
- 选择父代:根据适应度值选择个体作为父代。
- 交叉操作:选择的父代进行交叉,生成子代。
- 变异操作:对子代进行变异操作。
- 形成新种群:用子代替换部分或全部父代,形成新种群。
- 终止条件:判断是否满足终止条件,如达到最大迭代次数或找到满意的解。
以上过程不断重复,直到满足终止条件为止。
基于sko的使用
这里使用sko.GA包求np.sin(x1) * np.cos(x2) + np.exp(-(x12 + x22))最小值
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sko.GA import GA
def objective_function(solution):
x1, x2 = solution
return np.sin(x1) * np.cos(x2) + np.exp(-(x1**2 + x2**2))
##需要定义目标函数、变量个数、种群大小等参数,迭代次数,变量的取值范围
ga = GA(func=objective_function, n_dim=2, size_pop=300, max_iter=300, lb=[-1, -1], ub=[1, 1], precision=1e-6)
# 运行遗传算法并获取最优解
best_x, best_y = ga.run()
print('Best x:', best_x)
print('Best y:', best_y)
# 绘制收敛曲线
history = pd.DataFrame(ga.all_history_Y)
fig, ax = plt.subplots(1, 2, figsize=(20, 8))
# 绘制每次迭代的最优值
ax[0].plot(history.index, history.values, color='red')
ax[0].set_title('Convergence Curve of Genetic Algorithm')
ax[0].set_xlabel('Generation')
ax[0].set_ylabel('Best Value')
# 绘制最优值的累积最小值
ax[1].plot(history.index, history.min(axis=1).cummin(), color='blue', label='Cumulative Minimum')
ax[1].set_title('Cumulative Minimum of Best Values')
ax[1].set_xlabel('Generation')
ax[1].set_ylabel('Cumulative Minimum Value')
ax[1].legend()
plt.show()
遗传算法原理具体流程如下:
1.定义适应度函数
对于种群中的每个个体,使用适应度函数计算其适应度值。适应度函数根据问题的目标定义,评估每个个体的优劣。适应度值越高,表示个体越优越。
# 定义适应度函数
def fitness(x):
return np.sin(x) * np.cos(x) # 适应度函数用于寻找最大值
2.定义解码函数
由于遗传算法中数值都是用二进制来表示,所以就需要解码函数将二进制转换为10进制,这里涉及到映射的概念,其实就是先将原本的二进制数转化为10进制,再查看该10进制的值对应变量区间的值。并且原本的二进制越长,转化后的实数就越精确。
原本:[11111110]转换为10进制就是254
转换为10进制:
这样就是压缩到[0,1]区间,然后再进行归一化的逆运算
所以:
# 二进制解码函数
def decode(individual, x_min, x_max, bit_length):
decimal_value = int("".join(map(str, individual)), 2)
return x_min + (x_max - x_min) * decimal_value / (2**bit_length - 1)
3. 初始化种群(Initialization)
在遗传算法的开始阶段,首先需要生成初始种群。种群由一组个体组成,每个个体表示一个可能的解。初始化种群的方法通常是随机生成,即根据问题的解空间随机生成一定数量的个体。
def initialize_population(pop_size, bit_length):
return np.random.randint(0, 2, (pop_size, bit_length))
4. 选择父代(Selection)
根据适应度值,从种群中选择个体作为父代,以生成下一代个体。常用的选择方法有轮盘赌选择(Roulette Wheel Selection)、锦标赛选择(Tournament Selection)和排序选择(Rank Selection)等。
轮盘赌选择算法计算所有染色体的适应值,占据总适应值的百分比Pi来计算各个个体被选中的概率。
def selection(population, fitness_values):
min_fitness = np.min(fitness_values)
adjusted_fitness_values = fitness_values - min_fitness + 1e-10 # 确保非负
total_fitness = np.sum(adjusted_fitness_values)
probabilities = adjusted_fitness_values / total_fitness
selected_indices = np.random.choice(len(population), size=len(population), p=probabilities)
return population[selected_indices]
4. 交叉操作(Crossover)
选择的父代个体进行交叉操作,生成子代。交叉操作通过交换两个个体的部分基因,产生新的个体。常用的交叉方法有单点交叉(Single-point Crossover)、双点交叉(Two-point Crossover)和均匀交叉(Uniform Crossover)等。
这里使用单点交叉,并且染色体交叉一般是有概率的具体取这里(0.4~0.99之间)
# 交叉操作(单点交叉)
def crossover(parent1, parent2, crossover_prob=0.8):
if np.random.random() < crossover_prob:
crossover_point = np.random.randint(1, len(parent1))
child1 = np.concatenate([parent1[:crossover_point], parent2[crossover_point:]])
child2 = np.concatenate([parent2[:crossover_point], parent1[crossover_point:]])
else:
child1, child2 = parent1.copy(), parent2.copy()
return child1, child2
5. 变异操作(Mutation)
对子代进行变异操作,通过随机改变个体的一部分基因,增加种群的多样性。变异操作有助于防止算法陷入局部最优解。常用的变异方法有位翻转变异(Bit-flip Mutation)等。
def mutation(individual, mutation_rate=0.01):
for i in range(len(individual)):
if np.random.rand() < mutation_rate:
individual[i] = 1 - individual[i]
return individual
6.主程序
# 遗传算法主程序
def genetic_algorithm(pop_size, x_min, x_max, generations, bit_length, mutation_rate=0.01):
population = initialize_population(pop_size, bit_length)
for generation in range(generations):
decoded_population = np.array([decode(ind, x_min, x_max, bit_length) for ind in population])
fitness_values = np.array([fitness(ind) for ind in decoded_population])
population = selection(population, fitness_values)
new_population = []
for i in range(0, pop_size, 2):
parent1, parent2 = population[i], population[i+1]
child1, child2 = crossover(parent1, parent2)
new_population.extend([child1, child2])
population = np.array([mutation(ind, mutation_rate) for ind in new_population])
best_individual = population[np.argmax([fitness(decode(ind, x_min, x_max, bit_length)) for ind in population])]
best_solution = decode(best_individual, x_min, x_max, bit_length)
return best_solution, fitness(best_solution) # 返回最优个体和对应的最大值
7.运行代码
import numpy as np
# 定义适应度函数
def fitness(x):
return np.sin(x) * np.cos(x) # 适应度函数用于寻找最大值
# 二进制解码函数
def decode(individual, x_min, x_max, bit_length):
decimal_value = int("".join(map(str, individual)), 2)
return x_min + (x_max - x_min) * decimal_value / (2**bit_length - 1)
# 初始化种群
def initialize_population(pop_size, bit_length):
return np.random.randint(0, 2, (pop_size, bit_length))
# 选择操作(轮盘赌选择)
def selection(population, fitness_values):
min_fitness = np.min(fitness_values)
adjusted_fitness_values = fitness_values - min_fitness + 1e-10 # 确保非负
total_fitness = np.sum(adjusted_fitness_values)
probabilities = adjusted_fitness_values / total_fitness
selected_indices = np.random.choice(len(population), size=len(population), p=probabilities)
return population[selected_indices]
# 交叉操作(单点交叉)
def crossover(parent1, parent2, crossover_prob=0.8):
if np.random.random() < crossover_prob:
crossover_point = np.random.randint(1, len(parent1))
child1 = np.concatenate([parent1[:crossover_point], parent2[crossover_point:]])
child2 = np.concatenate([parent2[:crossover_point], parent1[crossover_point:]])
else:
child1, child2 = parent1.copy(), parent2.copy()
return child1, child2
# 变异操作
def mutation(individual, mutation_rate=0.01):
for i in range(len(individual)):
if np.random.rand() < mutation_rate:
individual[i] = 1 - individual[i]
return individual
# 遗传算法主程序
def genetic_algorithm(pop_size, x_min, x_max, generations, bit_length, mutation_rate=0.01):
population = initialize_population(pop_size, bit_length)
for generation in range(generations):
decoded_population = np.array([decode(ind, x_min, x_max, bit_length) for ind in population])
fitness_values = np.array([fitness(ind) for ind in decoded_population])
population = selection(population, fitness_values)
new_population = []
for i in range(0, pop_size, 2):
parent1, parent2 = population[i], population[i+1]
child1, child2 = crossover(parent1, parent2)
new_population.extend([child1, child2])
population = np.array([mutation(ind, mutation_rate) for ind in new_population])
best_individual = population[np.argmax([fitness(decode(ind, x_min, x_max, bit_length)) for ind in population])]
best_solution = decode(best_individual, x_min, x_max, bit_length)
return best_solution, fitness(best_solution) # 返回最优个体和对应的最大值
# 参数设置
pop_size = 100 # 种群规模
x_min = -1** np.pi # 搜索空间最小值
x_max = 1 * np.pi # 搜索空间最大值
generations = 100 # 最大迭代次数
bit_length = 16 # 二进制字符串长度
mutation_rate = 0.01 # 变异率
# 运行遗传算法
best_solution, max_value = genetic_algorithm(pop_size, x_min, x_max, generations, bit_length, mutation_rate)
print(f"最优解: x = {best_solution}, 最大值: f(x) = {max_value}")