
遗传算法:如何用“进化”解决复杂问题?
你有没有想过,大自然是怎么让生物变得越来越强大的?
比如,为什么长颈鹿的脖子越来越长,为什么鸟儿的翅膀能飞得越来越远??
图片
其实,大自然有一套神奇的“优化”方法,而科学家们把这个方法用到了计算机里,这就是“遗传算法”。
接下来,我们将深入探讨遗传算法的设计思想、基本原理和实践应用,帮助你更好地理解和应用这一强大的优化工具。
1.前言|什么是遗传算法?
遗传算法(Genetic Algorithm,简称GA)起源于对生物系统的计算机模拟研究,是一种随机全局搜索优化方法。
它模拟了生物进化过程中的选择、交叉和变异等现象,从初始种群出发,通过一系列操作,使群体逐渐进化到搜索空间中更优的区域,最终收敛到一群最适应环境的个体,从而求得问题的优质解。
图片
▲ 遗传算法原理示意图
简单来说,它就像大自然一样,通过“选择”“交配”和“变异”来找到解决问题的最好方法。
其中一些关键术语如下:
🔻种群(Population) 参与演化的生物群体,即解的搜索空间
🔹染色体(Chromosome) 对应问题的解向量
▪️ 基因(Gene) 解向量的一个分量,或者编码后的解向量的一位
▪️ 个体(Individual) 种群的每一个成员,对应每一个可能的解
🔹适应度(Fitness) 体现个体的生存能力,与目标函数相关的函数
🔻遗传算子(Operator) 个体的演化操作,包括选择、交叉、变异
🔹选择(Selection) 基于适应度的优胜劣汰,以一定的概率从种群中选择若干个体
🔹交叉(Crossover) 两个染色体进行基因重组
🔹变异(Mutation):单个染色体的基因以较低概率发生随机变化
2.原理|遗传算法是怎么工作的?
遗传算法就像是在玩一个“寻宝游戏”。一开始,我们有很多“寻宝者”(这些“寻宝者”就是算法中的“种群”),它们都在不同的地方寻找宝藏(也就是问题的最优解)。
图片
▲ 藏宝图
每个“寻宝者”都有自己的“地图”(这个“地图”就是“基因”,它决定了“寻宝者”的特征和能力)。
遗传算法也是这样工作的。
初始种群产生了一系列随机解,选择操作保证了搜索的方向性,交叉和变异拓宽了搜索空间,其中交叉操作延续父辈个体的优良基因,变异操作则可能产生比当前优势基因更优秀的个体。
图片
▲ 人类进化演变图
变异操作有利于跳出局部最优解,同时增加了随机搜索的概率,即容易发散。因此,遗传算法需要在过早收敛(早熟)和发散、精度和效率之间平衡。
遗传算法的核心在于其模拟生物进化过程的几个关键操作:
选择操作|Selection
根据个体的适应度,以一定的概率从种群中选择若干个个体作为下一代的父母。
适应度高的个体有更高的被选中概率,这类似于自然选择中的“适者生存”。
遗传算法会挑出那些“表现好”的个体,让它们有更多的机会繁殖后代。
常见方法:
- 轮盘赌选择(Roulette Wheel Selection)根据个体的适应度值分配一个概率区间,适应度高的个体获得更大的区间。通过随机选择,适应度高的个体被选中的概率更高。
- 锦标赛选择(Tournament Selection)随机选择若干个体进行“比赛”,适应度最高的个体获胜并进入下一代。
- 排名选择(Rank Selection)根据个体的适应度进行排名,排名靠前的个体有更高的选择概率。这种方法适用于适应度值差异较大的情况。
交叉操作|Crossover
两个父本个体的基因在某一位置处被切断,前后两串分别交叉组合,形成两个新的子代个体。这一过程类似于生物的有性繁殖,通过基因重组产生新的变异。
图片
▲ 基因交叉组合
两个“表现好”的个体组合起来,产生新的后代。这个过程就像是生物的有性繁殖,新的后代会继承父母的优点。
常见方法:
- 单点交叉(Single-point Crossover)在两个父代个体的染色体上随机选择一个交叉点,将交叉点之后的部分基因片段进行交换。
- 多点交叉(Multi-point Crossover)选择多个交叉点进行基因片段的交换。
- 均匀交叉(Uniform Crossover)随机决定每个基因位是否交换,使得基因片段的交换更加均匀。
变异操作|Mutation
对个体的基因序列进行随机变异,以一定的概率改变某个基因的值。这为种群引入了新的遗传信息,增加了种群的多样性,避免算法陷入局部最优。
偶尔,一些后代会发生随机的变化,这就像生物的基因突变。虽然大多数变异可能没什么用,但偶尔会有一些变异让后代变得更强大。
常见方法:
- 位变异(Bit-flip Mutation)随机选择一个基因位,将其值从0变为1或从1变为0(适用于二进制编码)。
- 均匀变异(Uniform Mutation)随机选择多个基因位进行变异。
- 高斯变异(Gaussian Mutation)对基因值进行高斯分布的随机扰动(适用于实数编码)。
3.应用|遗传算法有什么用?
遗传算法在优化问题、机器学习和工程设计等领域有广泛应用,
例如在资源分配、路径规划、调度问题、特征选择、神经网络训练、超参数优化、结构设计、电路设计和系统优化等方面能够快速找到接近最优的解。
图片
▲ 梯度下降算法
其优势在于全局搜索能力强,可有效避免局部最优;适应性强,适用于非线性、高维度和多峰问题;并行性高,适合并行计算。
旅行推销员问题(TSP)是遗传算法的经典应用之一。
TSP问题要求从n个城市中找到一条最短路径,使推销员从某城市出发,唯一走遍所有城市后回到起点。
以下是一个用遗传算法解决TSP问题的Python示例。
import numpy as np
import matplotlib.pyplot as plt
import random
from math import sqrt
from matplotlib.collections import LineCollection
plt.rcParams['font.family'] = ['serif'] # 显示中文问题
plt.rcParams['font.serif'] = ['SimSun'] # 显示中文问题
# 生成随机城市坐标
def generate_cities(num_cities, width=1000, height=1000):
return [(random.randint(0, width), random.randint(0, height)) for _ in range(num_cities)]
# 计算路径总距离
def calculate_distance(path, cities):
distance = 0
for i in range(len(path)):
x1, y1 = cities[path[i-1]]
x2, y2 = cities[path[i]]
distance += sqrt((x2 - x1)**2 + (y2 - y1)**2)
return distance
# 初始化种群
def initialize_population(pop_size, num_cities):
population = []
for _ in range(pop_size):
individual = list(range(num_cities))
random.shuffle(individual)
population.append(individual)
return population
# 选择操作 - 轮盘赌选择
def selection(population, cities, num_parents):
fitness_values = [1/calculate_distance(individual, cities) for individual in population]
total_fitness = sum(fitness_values)
probabilities = [f/total_fitness for f in fitness_values]
selected_indices = np.random.choice(len(population), size=num_parents, p=probabilities, replace=False)
return [population[i] for i in selected_indices]
# 交叉操作 - 有序交叉(OX)
def crossover(parent1, parent2):
size = len(parent1)
child = [-1] * size
# 选择交叉点
start, end = sorted(random.sample(range(size), 2))
# 从parent1复制片段
child[start:end] = parent1[start:end]
# 从parent2填充剩余城市
remaining = [city for city in parent2 if city not in child]
ptr = 0
for i in range(size):
if child[i] == -1:
child[i] = remaining[ptr]
ptr += 1
return child
# 变异操作 - 交换变异
def mutate(individual, mutation_rate):
if random.random() < mutation_rate:
i, j = random.sample(range(len(individual)), 2)
individual[i], individual[j] = individual[j], individual[i]
return individual
# 遗传算法主函数
def genetic_algorithm(cities, pop_size=100, num_generatinotallow=500, mutation_rate=0.01, elitism_ratio=0.1):
num_cities = len(cities)
population = initialize_population(pop_size, num_cities)
best_distance = float('inf')
best_path = None
fitness_history = []
num_elites = int(pop_size * elitism_ratio)
for generation in range(num_generations):
# 评估种群
distances = [calculate_distance(individual, cities) for individual in population]
current_best = min(distances)
fitness_history.append(current_best)
if current_best < best_distance:
best_distance = current_best
best_path = population[distances.index(current_best)]
# 选择精英
elite_indices = np.argsort(distances)[:num_elites]
elites = [population[i] for i in elite_indices]
# 选择父母
parents = selection(population, cities, pop_size - num_elites)
# 生成下一代
next_generation = elites.copy()
while len(next_generation) < pop_size:
parent1, parent2 = random.sample(parents, 2)
child = crossover(parent1, parent2)
child = mutate(child, mutation_rate)
next_generation.append(child)
population = next_generation
return best_path, best_distance, fitness_history
# 绘制路径
def plot_path(cities, path, title="TSP Path"):
path_coords = [cities[i] for i in path] + [cities[path[0]]] # 回到起点
x, y = zip(*path_coords)
fig, ax = plt.subplots(figsize=(10, 6))
ax.scatter(x, y, color='red')
# 绘制路径线
lines = [[path_coords[i], path_coords[i+1]] for i in range(len(path_coords)-1)]
lc = LineCollection(lines, colors='blue', linewidths=1)
ax.add_collection(lc)
ax.set_title(title)
ax.set_xlabel("X 轴")
ax.set_ylabel("Y 轴")
plt.grid()
plt.show()
# 绘制适应度进化曲线
def plot_fitness_history(fitness_history, title="适应度进化曲线"):
plt.figure(figsize=(10, 6))
plt.plot(fitness_history, color='green')
plt.title(title)
plt.xlabel("迭代次数")
plt.ylabel("最优距离")
plt.grid()
plt.show()
# 主程序
if __name__ == "__main__":
# 参数设置
num_cities = 20
pop_size = 100
num_generations = 500
mutation_rate = 0.02
# 生成城市
cities = generate_cities(num_cities)
# 运行遗传算法
best_path, best_distance, fitness_history = genetic_algorithm(
cities, pop_size=pop_size, num_generatinotallow=num_generations, mutation_rate=mutation_rate)
print(f"最优距离: {best_distance}")
print(f"最优路径: {best_path}")
# 绘制结果
plot_path(cities, best_path, f"旅行推销员问题(TSP) (最优距离: {best_distance:.2f})")
plot_fitness_history(fitness_history)
图片
图片
▲ 程序输出结果
这个实现提供了TSP问题的基本遗传算法解决方案,可以作为进一步优化的基础。
结语
遗传算法虽然听起来很复杂,但其实它的核心思想非常简单:通过模拟自然选择的力量,逐步找到问题的最优解。它不仅是一种优化算法,更是一种从自然中汲取智慧的创新方法。
它让我们看到了自然选择的力量,也让我们相信,通过模仿自然,我们可以找到解决复杂问题的钥匙。
本文转载自Fairy Girl,作者:Fairy Girl
