遺傳算法:如何用“進(jìn)化”解決復(fù)雜問題?
你有沒有想過,大自然是怎么讓生物變得越來越強大的?
比如,為什么長頸鹿的脖子越來越長,為什么鳥兒的翅膀能飛得越來越遠(yuǎn)??
圖片
其實,大自然有一套神奇的“優(yōu)化”方法,而科學(xué)家們把這個方法用到了計算機里,這就是“遺傳算法”。
接下來,我們將深入探討遺傳算法的設(shè)計思想、基本原理和實踐應(yīng)用,幫助你更好地理解和應(yīng)用這一強大的優(yōu)化工具。
1.前言|什么是遺傳算法?
遺傳算法(Genetic Algorithm,簡稱GA)起源于對生物系統(tǒng)的計算機模擬研究,是一種隨機全局搜索優(yōu)化方法。
它模擬了生物進(jìn)化過程中的選擇、交叉和變異等現(xiàn)象,從初始種群出發(fā),通過一系列操作,使群體逐漸進(jìn)化到搜索空間中更優(yōu)的區(qū)域,最終收斂到一群最適應(yīng)環(huán)境的個體,從而求得問題的優(yōu)質(zhì)解。
圖片
▲ 遺傳算法原理示意圖
簡單來說,它就像大自然一樣,通過“選擇”“交配”和“變異”來找到解決問題的最好方法。
其中一些關(guān)鍵術(shù)語如下:
??種群(Population) 參與演化的生物群體,即解的搜索空間
??染色體(Chromosome) 對應(yīng)問題的解向量
?? 基因(Gene) 解向量的一個分量,或者編碼后的解向量的一位
?? 個體(Individual) 種群的每一個成員,對應(yīng)每一個可能的解
??適應(yīng)度(Fitness) 體現(xiàn)個體的生存能力,與目標(biāo)函數(shù)相關(guān)的函數(shù)
??遺傳算子(Operator) 個體的演化操作,包括選擇、交叉、變異
??選擇(Selection) 基于適應(yīng)度的優(yōu)勝劣汰,以一定的概率從種群中選擇若干個體
??交叉(Crossover) 兩個染色體進(jìn)行基因重組
??變異(Mutation):單個染色體的基因以較低概率發(fā)生隨機變化
2.原理|遺傳算法是怎么工作的?
遺傳算法就像是在玩一個“尋寶游戲”。一開始,我們有很多“尋寶者”(這些“尋寶者”就是算法中的“種群”),它們都在不同的地方尋找寶藏(也就是問題的最優(yōu)解)。
圖片
▲ 藏寶圖
每個“尋寶者”都有自己的“地圖”(這個“地圖”就是“基因”,它決定了“尋寶者”的特征和能力)。
遺傳算法也是這樣工作的。
初始種群產(chǎn)生了一系列隨機解,選擇操作保證了搜索的方向性,交叉和變異拓寬了搜索空間,其中交叉操作延續(xù)父輩個體的優(yōu)良基因,變異操作則可能產(chǎn)生比當(dāng)前優(yōu)勢基因更優(yōu)秀的個體。
圖片
▲ 人類進(jìn)化演變圖
變異操作有利于跳出局部最優(yōu)解,同時增加了隨機搜索的概率,即容易發(fā)散。因此,遺傳算法需要在過早收斂(早熟)和發(fā)散、精度和效率之間平衡。
遺傳算法的核心在于其模擬生物進(jìn)化過程的幾個關(guān)鍵操作:
選擇操作|Selection
根據(jù)個體的適應(yīng)度,以一定的概率從種群中選擇若干個個體作為下一代的父母。
適應(yīng)度高的個體有更高的被選中概率,這類似于自然選擇中的“適者生存”。
遺傳算法會挑出那些“表現(xiàn)好”的個體,讓它們有更多的機會繁殖后代。
常見方法:
- 輪盤賭選擇(Roulette Wheel Selection)根據(jù)個體的適應(yīng)度值分配一個概率區(qū)間,適應(yīng)度高的個體獲得更大的區(qū)間。通過隨機選擇,適應(yīng)度高的個體被選中的概率更高。
- 錦標(biāo)賽選擇(Tournament Selection)隨機選擇若干個體進(jìn)行“比賽”,適應(yīng)度最高的個體獲勝并進(jìn)入下一代。
- 排名選擇(Rank Selection)根據(jù)個體的適應(yīng)度進(jìn)行排名,排名靠前的個體有更高的選擇概率。這種方法適用于適應(yīng)度值差異較大的情況。
交叉操作|Crossover
兩個父本個體的基因在某一位置處被切斷,前后兩串分別交叉組合,形成兩個新的子代個體。這一過程類似于生物的有性繁殖,通過基因重組產(chǎn)生新的變異。
圖片
▲ 基因交叉組合
兩個“表現(xiàn)好”的個體組合起來,產(chǎn)生新的后代。這個過程就像是生物的有性繁殖,新的后代會繼承父母的優(yōu)點。
常見方法:
- 單點交叉(Single-point Crossover)在兩個父代個體的染色體上隨機選擇一個交叉點,將交叉點之后的部分基因片段進(jìn)行交換。
- 多點交叉(Multi-point Crossover)選擇多個交叉點進(jìn)行基因片段的交換。
- 均勻交叉(Uniform Crossover)隨機決定每個基因位是否交換,使得基因片段的交換更加均勻。
變異操作|Mutation
對個體的基因序列進(jìn)行隨機變異,以一定的概率改變某個基因的值。這為種群引入了新的遺傳信息,增加了種群的多樣性,避免算法陷入局部最優(yōu)。
偶爾,一些后代會發(fā)生隨機的變化,這就像生物的基因突變。雖然大多數(shù)變異可能沒什么用,但偶爾會有一些變異讓后代變得更強大。
常見方法:
- 位變異(Bit-flip Mutation)隨機選擇一個基因位,將其值從0變?yōu)?或從1變?yōu)?(適用于二進(jìn)制編碼)。
- 均勻變異(Uniform Mutation)隨機選擇多個基因位進(jìn)行變異。
- 高斯變異(Gaussian Mutation)對基因值進(jìn)行高斯分布的隨機擾動(適用于實數(shù)編碼)。
3.應(yīng)用|遺傳算法有什么用?
遺傳算法在優(yōu)化問題、機器學(xué)習(xí)和工程設(shè)計等領(lǐng)域有廣泛應(yīng)用,
例如在資源分配、路徑規(guī)劃、調(diào)度問題、特征選擇、神經(jīng)網(wǎng)絡(luò)訓(xùn)練、超參數(shù)優(yōu)化、結(jié)構(gòu)設(shè)計、電路設(shè)計和系統(tǒng)優(yōu)化等方面能夠快速找到接近最優(yōu)的解。
圖片
▲ 梯度下降算法
其優(yōu)勢在于全局搜索能力強,可有效避免局部最優(yōu);適應(yīng)性強,適用于非線性、高維度和多峰問題;并行性高,適合并行計算。
旅行推銷員問題(TSP)是遺傳算法的經(jīng)典應(yīng)用之一。
TSP問題要求從n個城市中找到一條最短路徑,使推銷員從某城市出發(fā),唯一走遍所有城市后回到起點。
以下是一個用遺傳算法解決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'] # 顯示中文問題
# 生成隨機城市坐標(biāo)
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復(fù)制片段
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
# 遺傳算法主函數(shù)
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()
# 繪制適應(yīng)度進(jìn)化曲線
def plot_fitness_history(fitness_history, title="適應(yīng)度進(jìn)化曲線"):
plt.figure(figsize=(10, 6))
plt.plot(fitness_history, color='green')
plt.title(title)
plt.xlabel("迭代次數(shù)")
plt.ylabel("最優(yōu)距離")
plt.grid()
plt.show()
# 主程序
if __name__ == "__main__":
# 參數(shù)設(shè)置
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"最優(yōu)距離: {best_distance}")
print(f"最優(yōu)路徑: {best_path}")
# 繪制結(jié)果
plot_path(cities, best_path, f"旅行推銷員問題(TSP) (最優(yōu)距離: {best_distance:.2f})")
plot_fitness_history(fitness_history)
圖片
圖片
▲ 程序輸出結(jié)果
這個實現(xiàn)提供了TSP問題的基本遺傳算法解決方案,可以作為進(jìn)一步優(yōu)化的基礎(chǔ)。
結(jié)語
遺傳算法雖然聽起來很復(fù)雜,但其實它的核心思想非常簡單:通過模擬自然選擇的力量,逐步找到問題的最優(yōu)解。它不僅是一種優(yōu)化算法,更是一種從自然中汲取智慧的創(chuàng)新方法。
它讓我們看到了自然選擇的力量,也讓我們相信,通過模仿自然,我們可以找到解決復(fù)雜問題的鑰匙。
本文轉(zhuǎn)載自??Fairy Girl??,作者:Fairy Girl
