麻雀搜索算法(SSA):如何通過模擬麻雀行為提升全局搜索能力?
在大自然的舞臺上,麻雀或許是最不起眼的“演員”,但它們卻有著令人驚嘆的生存智慧。
今天,我們要聊的是一種從麻雀身上汲取靈感的神奇算法—麻雀搜索算法(SSA)。
深入探討SSA算法的靈感來源、基本原理、算法流程以及如何通過代碼實現它。
一、前言|麻雀搜索算法的來源
麻雀搜索算法(Sparrow Search Algorithm,SSA)是一種新型的群體智能優化算法,其靈感來源于麻雀的覓食和反捕食行為。
在自然界中,麻雀通常會分為以下幾種角色:
- 發現者(Producer):負責尋找食物,為群體提供覓食方向。
- 加入者(Scrounger):跟隨發現者獲取食物。
- 偵察者(Scout):負責監視周圍環境,當發現危險時發出警報并引導群體轉移到安全區域。
這些行為體現了麻雀群體的分工協作和動態適應性,為算法的設計提供了基礎。
麻雀搜索算法由東華大學的Xue和Shen于2020年首次提出。
自提出以來,該算法因其高效性和適應性受到了廣泛關注,并在多個領域得到了應用。
例如電力負荷預測、無人機航跡規劃、圖像處理和神經網絡參數優化等。
隨著研究的深入,許多學者對麻雀搜索算法進行了改進,以提高其全局搜索能力和收斂速度。
例如,引入混沌初始化策略、動態慣性權重等改進方法。
這些改進使得麻雀搜索算法在解決復雜優化問題時表現更加出色。
二、原理|麻雀搜索算法的原理
麻雀搜索算法(SSA)通過模擬麻雀的群體覓食行為,成功地避免了局部最優,展現出強大的優化能力?。
麻雀群體在覓食過程中表現出以下特點:
- 麻雀群體中存在分工,包括發現者(Producer)、加入者(Scrounger)和偵察者(Scout)。
- 發現者負責尋找食物豐富的區域,加入者跟隨發現者獲取食物,偵察者負責監測環境中的危險。
- 當發現危險時,偵察者會引導群體轉移到安全區域,避免被捕食。
▲ 麻雀優化算法動態可視化
SSA通過模擬這些行為來實現優化,其核心在于通過全局搜索和局部搜索的平衡,以及反捕食機制來避免陷入局部最優。
01 數學原理|Theory
接下來,將詳細地描述麻雀搜索算法的數學原理,包括發現者、加入者和偵察者的位置更新公式及其背后的邏輯。
3. 加入者的位置更新
加入者跟隨發現者獲取食物,其位置更新公式為:
4. 偵察者的位置更新
偵察者負責監測環境中的危險,其位置更新公式為:
02 算法流程|Process
麻雀搜索算法的開發流程描述如下:
三、實踐|麻雀搜索算法應用
麻雀搜索算法是一種受麻雀覓食和反捕食行為啟發的群體智能優化算法。
下面我將用Python實現一個簡單的SSA案例,用于求解Ackley函數優化問題,并提供完整的實現代碼和可視化。
01 問題定義|Definition
Ackley函數是一個常用的多峰測試函數,其全局最小值在原點(0,0,...,0)處,函數值為0。
該函數具有許多局部極小值,對優化算法是一個很好的測試。
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函數
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': []
}
# 找出當前最佳和最差
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())
# 迭代參數
ST = 0.6# 安全閾值
PD = 0.7# 發現者比例
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} | 當前最佳適應度: {best_fitness:.6f}")
# 更新發現者位置
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)
# 重新計算適應度
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 算法運行|Runing
接下來將進行參數設置、運行麻雀搜索算法。并實現一個動態可視化版本,展示隨著最優適應度曲線的變化,目標函數搜索過程的動態演化。
# ==================================================
print("準備運行麻雀搜索算法...")
print("="*50)
print("麻雀搜索算法 (SSA) 開始運行")
print(f"麻雀數量: {30}")
print(f"最大迭代次數: {50}")
print(f"搜索空間維度: {2}")
print(f"搜索范圍: [-5, 5]")
print("="*50)
time.sleep(1)
# 參數設置
dim = 2
lb = -5
ub = 5
max_iter = 50
n_sparrows = 30
# 運行算法并記錄歷史
history = sparrow_search_algorithm_with_history(ackley, dim, lb, ub, max_iter, n_sparrows)
# 最終結果
print("="*50)
print("優化完成!")
print(f"找到的最佳解: [{history['best_positions'][-1][0]:.4f}{history['best_positions'][-1][1]:.4f}]")
print(f"最佳適應度值: {history['best_fitness'][-1]:.6f}")
print("="*50)
time.sleep(1)
print("生成可視化結果...")
time.sleep(1)
# ==================================================
# 準備可視化數據
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]])
# 創建圖形
plt.rcParams['font.sans-serif'] = ['SimHei'] # 設置中文顯示
plt.rcParams['axes.unicode_minus'] = False# 解決負號顯示問題
fig = plt.figure(figsize=(18, 6), dpi=100)
fig.suptitle('麻雀搜索算法動態可視化', fnotallow=16)
# 統一子圖尺寸
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='函數值')
scatter = ax1.scatter([], [], [], c='red', s=50, label='麻雀位置')
best_scatter = ax1.scatter([], [], [], c='blue', marker='*', s=200, label='最優解')
ax1.set_title('3D函數曲面與種群分布', 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='函數值')
scatter2d = ax2.scatter([], [], c='red', s=30, label='麻雀位置')
best_scatter2d = ax2.scatter([], [], c='blue', marker='*', s=100, label='最優解')
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='最佳適應度')
current_point = ax3.scatter([], [], c='red', s=50, label='當前值')
ax3.set_title('適應度收斂曲線', fnotallow=12)
ax3.set_xlabel('迭代次數', fnotallow=10)
ax3.set_ylabel('適應度值', 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)
# 參數顯示
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()
# 動畫更新函數
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"迭代次數: {frame}\n"
info += f"最佳適應度: {history['best_fitness'][frame]:.6f}\n"
info += f"最佳位置: [{best_pos[0]:.4f}, {best_pos[1]:.4f}]\n"
info += f"麻雀數量: {len(current_pos)}\n"
info += f"發現者比例: 70%\n"
info += f"警戒者比例: 20%"
info_text.set_text(info)
return scatter, best_scatter, scatter2d, best_scatter2d, convergence_line, current_point, info_text
# 創建動畫
ani = FuncAnimation(fig, update, frames=max_iter+1, interval=500, blit=True)
# 顯示動畫
plt.close()
HTML(ani.to_jshtml())
結果顯示|結果可視化
盡管麻雀搜索算法具有許多優點,但在處理復雜優化問題時仍存在一些不足,例如全局搜索能力較弱、容易陷入局部最優等。
為此,研究人員提出了多種改進策略:
- 混沌初始化:利用混沌序列(如立方映射、Tent混沌序列)初始化種群,提高種群的多樣性和分布均勻性。
- 引入其他算法機制:結合蝴蝶優化算法(BOA)、灰狼優化算法(GWO)等其他智能優化算法的機制,增強全局搜索能力。
- 動態慣性權重:引入動態慣性權重,平衡算法在迭代過程中的全局探索和局部搜索能力。
- 正余弦和柯西變異:在發現者位置更新中引入正余弦策略,在加入者位置中引入柯西變異,提高算法的全局尋優能力和收斂速度。
結語
麻雀搜索算法通過模擬麻雀的覓食和反捕食行為,利用發現者和追隨者的協作機制,逐步逼近全局最優解。它不僅原理直觀,而且實現簡單,適用于多種復雜的優化問題。
本文轉載自????Fairy Girlhub????,作者:Fairy Girlhub
