麻雀搜索算法(SSA):如何通過模擬麻雀行為提升全局搜索能力?
在大自然的舞臺上,麻雀或許是最不起眼的“演員”,但它們卻有著令人驚嘆的生存智慧。
今天,我們要聊的是一種從麻雀身上汲取靈感的神奇算法—麻雀搜索算法(SSA)。
深入探討SSA算法的靈感來源、基本原理、算法流程以及如何通過代碼實(shí)現(xiàn)它。
一、前言|麻雀搜索算法的來源
麻雀搜索算法(Sparrow Search Algorithm,SSA)是一種新型的群體智能優(yōu)化算法,其靈感來源于麻雀的覓食和反捕食行為。
在自然界中,麻雀通常會分為以下幾種角色:
- 發(fā)現(xiàn)者(Producer):負(fù)責(zé)尋找食物,為群體提供覓食方向。
- 加入者(Scrounger):跟隨發(fā)現(xiàn)者獲取食物。
- 偵察者(Scout):負(fù)責(zé)監(jiān)視周圍環(huán)境,當(dāng)發(fā)現(xiàn)危險時發(fā)出警報并引導(dǎo)群體轉(zhuǎn)移到安全區(qū)域。
這些行為體現(xiàn)了麻雀群體的分工協(xié)作和動態(tài)適應(yīng)性,為算法的設(shè)計提供了基礎(chǔ)。
麻雀搜索算法由東華大學(xué)的Xue和Shen于2020年首次提出。
自提出以來,該算法因其高效性和適應(yīng)性受到了廣泛關(guān)注,并在多個領(lǐng)域得到了應(yīng)用。
例如電力負(fù)荷預(yù)測、無人機(jī)航跡規(guī)劃、圖像處理和神經(jīng)網(wǎng)絡(luò)參數(shù)優(yōu)化等。
隨著研究的深入,許多學(xué)者對麻雀搜索算法進(jìn)行了改進(jìn),以提高其全局搜索能力和收斂速度。
例如,引入混沌初始化策略、動態(tài)慣性權(quán)重等改進(jìn)方法。
這些改進(jìn)使得麻雀搜索算法在解決復(fù)雜優(yōu)化問題時表現(xiàn)更加出色。
二、原理|麻雀搜索算法的原理
麻雀搜索算法(SSA)通過模擬麻雀的群體覓食行為,成功地避免了局部最優(yōu),展現(xiàn)出強(qiáng)大的優(yōu)化能力?。
麻雀群體在覓食過程中表現(xiàn)出以下特點(diǎn):
- 麻雀群體中存在分工,包括發(fā)現(xiàn)者(Producer)、加入者(Scrounger)和偵察者(Scout)。
- 發(fā)現(xiàn)者負(fù)責(zé)尋找食物豐富的區(qū)域,加入者跟隨發(fā)現(xiàn)者獲取食物,偵察者負(fù)責(zé)監(jiān)測環(huán)境中的危險。
- 當(dāng)發(fā)現(xiàn)危險時,偵察者會引導(dǎo)群體轉(zhuǎn)移到安全區(qū)域,避免被捕食。
▲ 麻雀優(yōu)化算法動態(tài)可視化
SSA通過模擬這些行為來實(shí)現(xiàn)優(yōu)化,其核心在于通過全局搜索和局部搜索的平衡,以及反捕食機(jī)制來避免陷入局部最優(yōu)。
01 數(shù)學(xué)原理|Theory
接下來,將詳細(xì)地描述麻雀搜索算法的數(shù)學(xué)原理,包括發(fā)現(xiàn)者、加入者和偵察者的位置更新公式及其背后的邏輯。
3. 加入者的位置更新
加入者跟隨發(fā)現(xiàn)者獲取食物,其位置更新公式為:
4. 偵察者的位置更新
偵察者負(fù)責(zé)監(jiān)測環(huán)境中的危險,其位置更新公式為:
02 算法流程|Process
麻雀搜索算法的開發(fā)流程描述如下:
三、實(shí)踐|麻雀搜索算法應(yīng)用
麻雀搜索算法是一種受麻雀覓食和反捕食行為啟發(fā)的群體智能優(yōu)化算法。
下面我將用Python實(shí)現(xiàn)一個簡單的SSA案例,用于求解Ackley函數(shù)優(yōu)化問題,并提供完整的實(shí)現(xiàn)代碼和可視化。
01 問題定義|Definition
Ackley函數(shù)是一個常用的多峰測試函數(shù),其全局最小值在原點(diǎn)(0,0,...,0)處,函數(shù)值為0。
該函數(shù)具有許多局部極小值,對優(yōu)化算法是一個很好的測試。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
import time
# Ackley函數(shù)
defackley(x):
a = 20
b = 0.2
c = 2 * np.pi
d = len(x)
sum_sq = sum([xi**2for xi in x])
sum_cos = sum([np.cos(c * xi) for xi in x])
term1 = -a * np.exp(-b * np.sqrt(sum_sq / d))
term2 = -np.exp(sum_cos / d)
return term1 + term2 + a + np.exp(1)
02 算法建模|Modeling
麻雀搜索算法的建模過程如下:
# 麻雀搜索算法
defsparrow_search_algorithm_with_history(obj_func, dim, lb, ub, max_iter, n_sparrows):
# 初始化
positions = np.random.uniform(lb, ub, (n_sparrows, dim))
fitness = np.array([obj_func(p) for p in positions])
# 歷史記錄
history = {
'positions': [positions.copy()],
'best_positions': [],
'best_fitness': [],
'worst_positions': []
}
# 找出當(dāng)前最佳和最差
best_index = np.argmin(fitness)
worst_index = np.argmax(fitness)
best_position = positions[best_index].copy()
best_fitness = fitness[best_index]
worst_position = positions[worst_index].copy()
history['best_positions'].append(best_position.copy())
history['best_fitness'].append(best_fitness)
history['worst_positions'].append(worst_position.copy())
# 迭代參數(shù)
ST = 0.6# 安全閾值
PD = 0.7# 發(fā)現(xiàn)者比例
SD = 0.2# 警戒者比例
n_p = int(n_sparrows * PD)
n_s = int(n_sparrows * SD)
# 迭代過程
for t inrange(max_iter):
if t % 10 == 0or t == max_iter-1or t == 0:
print(f"迭代 {t+1:3d}/{max_iter} | 當(dāng)前最佳適應(yīng)度: {best_fitness:.6f}")
# 更新發(fā)現(xiàn)者位置
R2 = np.random.rand()
for i inrange(n_p):
if R2 < ST:
alpha = np.random.randn()
for d inrange(dim):
positions[i,d] *= np.exp(-alpha * t / max_iter)
else:
Q = np.random.randn()
for d inrange(dim):
positions[i,d] += Q * np.random.rand()
positions[i] = np.clip(positions[i], lb, ub)
# 更新跟隨者位置
for i inrange(n_p, n_sparrows):
if i > n_sparrows / 2:
Q = np.random.randn()
for d inrange(dim):
positions[i,d] = Q * np.exp((worst_position[d] - positions[i,d]) / i**2)
else:
A = np.random.randint(-1, 2, dim)
A_plus = A.T @ np.linalg.pinv(A @ A.T)
for d inrange(dim):
positions[i,d] = best_position[d] + np.abs(positions[i,d] - best_position[d]) * A_plus[d]
positions[i] = np.clip(positions[i], lb, ub)
# 更新警戒者位置
for i inrange(n_s):
if fitness[i] > best_fitness:
beta = np.random.randn()
for d inrange(dim):
positions[i,d] = best_position[d] + beta * np.abs(positions[i,d] - best_position[d])
elif fitness[i] == best_fitness:
K = np.random.rand() * 2 - 1
for d inrange(dim):
positions[i,d] = positions[i,d] + K * (np.abs(positions[i,d] - worst_position[d]) /
(fitness[i] - worst_position[d] + 1e-50))
positions[i] = np.clip(positions[i], lb, ub)
# 重新計算適應(yīng)度
fitness = np.array([obj_func(p) for p in positions])
# 更新全局最佳和最差
current_best_index = np.argmin(fitness)
current_worst_index = np.argmax(fitness)
if fitness[current_best_index] < best_fitness:
best_position = positions[current_best_index].copy()
best_fitness = fitness[current_best_index]
worst_position = positions[current_worst_index].copy()
# 記錄歷史
history['positions'].append(positions.copy())
history['best_positions'].append(best_position.copy())
history['best_fitness'].append(best_fitness)
history['worst_positions'].append(worst_position.copy())
return history
03 算法運(yùn)行|Runing
接下來將進(jìn)行參數(shù)設(shè)置、運(yùn)行麻雀搜索算法。并實(shí)現(xiàn)一個動態(tài)可視化版本,展示隨著最優(yōu)適應(yīng)度曲線的變化,目標(biāo)函數(shù)搜索過程的動態(tài)演化。
# ==================================================
print("準(zhǔn)備運(yùn)行麻雀搜索算法...")
print("="*50)
print("麻雀搜索算法 (SSA) 開始運(yùn)行")
print(f"麻雀數(shù)量: {30}")
print(f"最大迭代次數(shù): {50}")
print(f"搜索空間維度: {2}")
print(f"搜索范圍: [-5, 5]")
print("="*50)
time.sleep(1)
# 參數(shù)設(shè)置
dim = 2
lb = -5
ub = 5
max_iter = 50
n_sparrows = 30
# 運(yùn)行算法并記錄歷史
history = sparrow_search_algorithm_with_history(ackley, dim, lb, ub, max_iter, n_sparrows)
# 最終結(jié)果
print("="*50)
print("優(yōu)化完成!")
print(f"找到的最佳解: [{history['best_positions'][-1][0]:.4f}{history['best_positions'][-1][1]:.4f}]")
print(f"最佳適應(yīng)度值: {history['best_fitness'][-1]:.6f}")
print("="*50)
time.sleep(1)
print("生成可視化結(jié)果...")
time.sleep(1)
# ==================================================
# 準(zhǔn)備可視化數(shù)據(jù)
x = np.linspace(lb, ub, 100)
y = np.linspace(lb, ub, 100)
X, Y = np.meshgrid(x, y)
Z = np.zeros_like(X)
for i inrange(X.shape[0]):
for j inrange(X.shape[1]):
Z[i,j] = ackley([X[i,j], Y[i,j]])
# 創(chuàng)建圖形
plt.rcParams['font.sans-serif'] = ['SimHei'] # 設(shè)置中文顯示
plt.rcParams['axes.unicode_minus'] = False# 解決負(fù)號顯示問題
fig = plt.figure(figsize=(18, 6), dpi=100)
fig.suptitle('麻雀搜索算法動態(tài)可視化', fnotallow=16)
# 統(tǒng)一子圖尺寸
gs = fig.add_gridspec(2, 3, width_ratios=[1, 1, 1], height_ratios=[1, 1])
# 3D曲面圖
ax1 = fig.add_subplot(gs[:, 0], projectinotallow='3d')
surf = ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.6)
fig.colorbar(surf, ax=ax1, shrink=0.6, aspect=10, label='函數(shù)值')
scatter = ax1.scatter([], [], [], c='red', s=50, label='麻雀位置')
best_scatter = ax1.scatter([], [], [], c='blue', marker='*', s=200, label='最優(yōu)解')
ax1.set_title('3D函數(shù)曲面與種群分布', fnotallow=12)
ax1.set_xlabel('x1', fnotallow=10)
ax1.set_ylabel('x2', fnotallow=10)
ax1.set_zlabel('f(x)', fnotallow=10)
ax1.legend(loc='upper right', fnotallow=8)
# 2D等高線圖
ax2 = fig.add_subplot(gs[:, 1])
contour = ax2.contourf(X, Y, Z, levels=50, cmap='viridis')
fig.colorbar(contour, ax=ax2, shrink=0.6, aspect=10, label='函數(shù)值')
scatter2d = ax2.scatter([], [], c='red', s=30, label='麻雀位置')
best_scatter2d = ax2.scatter([], [], c='blue', marker='*', s=100, label='最優(yōu)解')
ax2.set_title('2D等高線與種群分布', fnotallow=12)
ax2.set_xlabel('x1', fnotallow=10)
ax2.set_ylabel('x2', fnotallow=10)
ax2.legend(loc='upper right', fnotallow=8)
# 收斂曲線
ax3 = fig.add_subplot(gs[0, 2])
convergence_line, = ax3.plot([], [], 'b-', linewidth=2, label='最佳適應(yīng)度')
current_point = ax3.scatter([], [], c='red', s=50, label='當(dāng)前值')
ax3.set_title('適應(yīng)度收斂曲線', fnotallow=12)
ax3.set_xlabel('迭代次數(shù)', fnotallow=10)
ax3.set_ylabel('適應(yīng)度值', fnotallow=10)
ax3.grid(True, linestyle='--', alpha=0.6)
ax3.set_xlim(0, max_iter)
ax3.set_ylim(0, max(history['best_fitness']))
ax3.legend(loc='upper right', fnotallow=8)
# 參數(shù)顯示
ax4 = fig.add_subplot(gs[1, 2])
ax4.axis('off')
info_text = ax4.text(0.1, 0.5, '', fnotallow=10, bbox=dict(facecolor='white', alpha=0.8))
plt.tight_layout()
# 動畫更新函數(shù)
defupdate(frame):
# 更新3D圖
current_pos = history['positions'][frame]
current_z = np.array([ackley(p) for p in current_pos])
scatter._offsets3d = (current_pos[:,0], current_pos[:,1], current_z)
best_pos = history['best_positions'][frame]
best_z = ackley(best_pos)
best_scatter._offsets3d = ([best_pos[0]], [best_pos[1]], [best_z])
# 更新2D圖
scatter2d.set_offsets(current_pos)
best_scatter2d.set_offsets([best_pos])
# 更新收斂曲線
x_data = range(frame+1)
y_data = history['best_fitness'][:frame+1]
convergence_line.set_data(x_data, y_data)
current_point.set_offsets([[frame, history['best_fitness'][frame]]])
# 更新文本信息
info = f"迭代次數(shù): {frame}\n"
info += f"最佳適應(yīng)度: {history['best_fitness'][frame]:.6f}\n"
info += f"最佳位置: [{best_pos[0]:.4f}, {best_pos[1]:.4f}]\n"
info += f"麻雀數(shù)量: {len(current_pos)}\n"
info += f"發(fā)現(xiàn)者比例: 70%\n"
info += f"警戒者比例: 20%"
info_text.set_text(info)
return scatter, best_scatter, scatter2d, best_scatter2d, convergence_line, current_point, info_text
# 創(chuàng)建動畫
ani = FuncAnimation(fig, update, frames=max_iter+1, interval=500, blit=True)
# 顯示動畫
plt.close()
HTML(ani.to_jshtml())
結(jié)果顯示|結(jié)果可視化
盡管麻雀搜索算法具有許多優(yōu)點(diǎn),但在處理復(fù)雜優(yōu)化問題時仍存在一些不足,例如全局搜索能力較弱、容易陷入局部最優(yōu)等。
為此,研究人員提出了多種改進(jìn)策略:
- 混沌初始化:利用混沌序列(如立方映射、Tent混沌序列)初始化種群,提高種群的多樣性和分布均勻性。
- 引入其他算法機(jī)制:結(jié)合蝴蝶優(yōu)化算法(BOA)、灰狼優(yōu)化算法(GWO)等其他智能優(yōu)化算法的機(jī)制,增強(qiáng)全局搜索能力。
- 動態(tài)慣性權(quán)重:引入動態(tài)慣性權(quán)重,平衡算法在迭代過程中的全局探索和局部搜索能力。
- 正余弦和柯西變異:在發(fā)現(xiàn)者位置更新中引入正余弦策略,在加入者位置中引入柯西變異,提高算法的全局尋優(yōu)能力和收斂速度。
結(jié)語
麻雀搜索算法通過模擬麻雀的覓食和反捕食行為,利用發(fā)現(xiàn)者和追隨者的協(xié)作機(jī)制,逐步逼近全局最優(yōu)解。它不僅原理直觀,而且實(shí)現(xiàn)簡單,適用于多種復(fù)雜的優(yōu)化問題。
本文轉(zhuǎn)載自????Fairy Girlhub????,作者:Fairy Girlhub
