零基礎小白看懂粒子群優化算法(PSO)
一、什么是粒子群優化算法?
簡單說,粒子群優化算法(PSO)是一種模擬鳥群 / 魚群覓食的智能算法。想象一群鳥在找食物:
- 每只鳥(叫 “粒子”)不知道食物在哪,但能看到自己飛過的地方中 “最可能有食物” 的位置(個體經驗);
- 也能看到整個群體中 “最可能有食物” 的位置(群體經驗);
- 它們會根據這兩個經驗調整飛行方向和速度,慢慢靠近食物(最優解)。
PSO 就是用這種思路解決 “找最優解” 的問題,比如 “如何讓某個函數的結果最小(或最大)”。
二、核心概念拆解
粒子:算法中的 “搜索者”,每個粒子有兩個關鍵屬性:
- 位置:當前在搜索空間中的坐標(比如在三維空間中,位置可以用 (x1, x2, x3) 表示);
- 速度:下一步移動的方向和快慢。
適應度函數:判斷 “這個位置好不好” 的標準。比如我們想最小化函數
f(x) = -10x1 - e^(-x2/10 - x3)
,那每個粒子的位置代入這個函數后,結果越小,說明位置越 “好”。個體最優(p_best):每個粒子自己飛過的所有位置中,適應度最好的那個位置(“我之前飛過的地方里,這里最可能有食物”)。
全局最優(g_best):整個群體所有粒子飛過的位置中,適應度最好的那個位置(“大家飛過的地方里,這里最可能有食物”)。
位置和速度更新:粒子每次移動時,會根據以下規則調整速度和位置:
- 速度 = 慣性(保持之前的速度) + 個體經驗(向自己的 p_best 靠近) + 群體經驗(向全局的 g_best 靠近);
- 新位置 = 當前位置 + 新速度。
就像鳥飛的時候,既不會完全忘記之前的方向(慣性),也會參考自己和同伴的最佳經驗調整方向。
三、算法怎么跑起來?(用例子和代碼說話)
假設我們要解決的問題是:最小化函數f(x) = -10x1 - e^(-x2/10 - x3)
,其中 x1 的范圍是 0-1,x2 是 1-80,x3 是 0-120。
1. 初始化
先創建 100 個粒子(N=100),每個粒子有 3 個維度(x1, x2, x3,即 D=3);隨機給每個粒子分配初始位置(在上述范圍內)和初始速度(比如 - 1 到 1 之間)。
在代碼中,初始化的實現如下:
# 導入必要的庫
import numpy as np # 用于數值計算
import random # 用于生成隨機數
import math # 用于數學運算
import matplotlib.pyplot as plt # 用于繪圖# 2. 初始化粒子的位置和速度
def initialize_particles(num_particles, num_dimensions, pos_low, pos_high, vel_low, vel_high):"""參數說明:- num_particles:粒子數量- num_dimensions:變量維度(這里是3,因為有x1、x2、x3)- pos_low/pos_high:每個變量的位置范圍(列表形式)- vel_low/vel_high:速度范圍"""# 初始化位置矩陣(行數=粒子數,列數=維度)positions = np.zeros((num_particles, num_dimensions))# 初始化速度矩陣velocities = np.zeros((num_particles, num_dimensions))for i in range(num_particles): # 遍歷每個粒子for j in range(num_dimensions): # 遍歷每個維度(變量)# 位置在[pos_low[j], pos_high[j]]范圍內隨機生成positions[i][j] = random.uniform(pos_low[j], pos_high[j])# 速度在[vel_low, vel_high]范圍內隨機生成velocities[i][j] = random.uniform(vel_low, vel_high)return positions, velocities
2. 定義適應度函數
適應度函數是判斷位置好壞的標準,對于我們要最小化的函數,代碼定義如下:
# 1. 定義適應度函數(目標函數)
# 這里要最小化的函數:f(x) = -10*x1 - e^(-x2/10 - x3)
def fitness(x):# x是一個列表,包含3個變量:x[0]是x1,x[1]是x2,x[2]是x3x1, x2, x3 = x[0], x[1], x[2]return -10 * x1 - math.exp(-x2/10 - x3) # 計算函數值
3. 找初始最優
計算每個粒子的適應度(代入函數算結果);每個粒子的初始 “個體最優” 就是自己的初始位置;從所有粒子中挑出適應度最小的位置,作為 “全局最優”。
這部分在主函數中初始化個體最優和全局最優時實現:
# 初始化個體最優:一開始每個粒子的最優位置就是自己的初始位置
p_best_pos = positions.copy() # 個體最優位置矩陣
# 計算每個粒子的初始適應度(目標函數值)
p_best_val = np.array([fitness(pos) for pos in positions]) # 個體最優值數組# 初始化全局最優:從個體最優中找最好的
g_best_idx = np.argmin(p_best_val) # 找到適應度最小的索引(因為我們要最小化函數)
g_best_pos = p_best_pos[g_best_idx].copy() # 全局最優位置
g_best_val = p_best_val[g_best_idx] # 全局最優值
4. 迭代優化(重復 100 次,即 M=100)
每個粒子根據 “慣性、個體最優、全局最優” 更新速度和位置;每次移動后,重新計算適應度,如果比自己之前的 “個體最優” 更好,就更新個體最優;再從所有個體最優中挑出最好的,更新全局最優。
速度和位置更新的代碼實現:
# 3. 更新粒子的位置和速度
def update_particles(positions, velocities, p_best_pos, g_best_pos, w, c1, c2, pos_low, pos_high, vel_low, vel_high):"""參數說明:- p_best_pos:每個粒子的個體最優位置- g_best_pos:全局最優位置- w:慣性權重(控制對之前速度的保留程度)- c1、c2:學習因子(控制個體/群體經驗的影響程度)"""num_particles, num_dimensions = positions.shape # 獲取粒子數和維度for i in range(num_particles): # 遍歷每個粒子for j in range(num_dimensions): # 遍歷每個維度# 生成0-1之間的隨機數(增加搜索隨機性)r1 = random.uniform(0, 1)r2 = random.uniform(0, 1)# 速度更新公式:慣性 + 個體經驗 + 群體經驗velocities[i][j] = (w * velocities[i][j] # 慣性部分+ c1 * r1 * (p_best_pos[i][j] - positions[i][j]) # 向個體最優靠近+ c2 * r2 * (g_best_pos[j] - positions[i][j])) # 向全局最優靠近# 限制速度在[vel_low, vel_high]范圍內(避免速度過大)velocities[i][j] = max(min(velocities[i][j], vel_high), vel_low)# 位置更新公式:當前位置 + 新速度positions[i][j] += velocities[i][j]# 限制位置在[pos_low[j], pos_high[j]]范圍內(避免超出變量定義域)positions[i][j] = max(min(positions[i][j], pos_high[j]), pos_low[j])return positions, velocities
5. 算法主函數
將上述步驟整合到主函數中,實現完整的 PSO 算法:
# 4. 粒子群優化算法主函數
def pso_algorithm(num_particles, num_dimensions, max_iter, pos_low, pos_high, vel_low, vel_high, w=0.9, c1=2, c2=2):"""參數說明:- max_iter:最大迭代次數(搜索多少次)- 其他參數同上"""# 初始化粒子位置和速度positions, velocities = initialize_particles(num_particles, num_dimensions, pos_low, pos_high, vel_low, vel_high)# 初始化個體最優:一開始每個粒子的最優位置就是自己的初始位置p_best_pos = positions.copy() # 個體最優位置矩陣# 計算每個粒子的初始適應度(目標函數值)p_best_val = np.array([fitness(pos) for pos in positions]) # 個體最優值數組# 初始化全局最優:從個體最優中找最好的g_best_idx = np.argmin(p_best_val) # 找到適應度最小的索引(因為我們要最小化函數)g_best_pos = p_best_pos[g_best_idx].copy() # 全局最優位置g_best_val = p_best_val[g_best_idx] # 全局最優值# 記錄每次迭代的最優值(用于畫圖)best_values = [g_best_val]# 開始迭代搜索for iter in range(max_iter):# 更新粒子位置和速度positions, velocities = update_particles(positions, velocities, p_best_pos, g_best_pos, w, c1, c2, pos_low, pos_high, vel_low, vel_high)# 更新個體最優和全局最優for i in range(num_particles):current_val = fitness(positions[i]) # 當前位置的適應度# 如果當前位置比個體最優好(值更小),就更新個體最優if current_val < p_best_val[i]:p_best_pos[i] = positions[i].copy()p_best_val[i] = current_val# 從個體最優中找新的全局最優current_g_best_idx = np.argmin(p_best_val)current_g_best_val = p_best_val[current_g_best_idx]# 如果新的全局最優更好,就更新全局最優if current_g_best_val < g_best_val:g_best_pos = p_best_pos[current_g_best_idx].copy()g_best_val = current_g_best_val# 記錄當前迭代的最優值best_values.append(g_best_val)# 打印迭代信息(每10次打印一次,避免輸出太多)if (iter + 1) % 10 == 0:print(f"迭代第{iter+1}次,當前最優值:{g_best_val:.6f}")# 畫收斂曲線(最優值隨迭代次數的變化)plt.figure(figsize=(8, 5))plt.plot(best_values)plt.xlabel("迭代次數")plt.ylabel("最優目標函數值")plt.title("PSO算法收斂曲線")plt.grid(True)plt.show()return g_best_pos, g_best_val # 返回最終找到的最優位置和最優值
6. 運行程序并查看結果
通過主程序調用 PSO 算法,設置參數并運行:
# 5. 主程序:運行PSO算法
if __name__ == "__main__":# 問題設置num_particles = 100 # 粒子數量(100個粒子一起搜索)num_dimensions = 3 # 變量維度(x1, x2, x3三個變量)max_iter = 100 # 最大迭代次數(搜索100次)# 變量范圍:x1∈[0,1],x2∈[1,80],x3∈[0,120]pos_low = [0, 1, 0]pos_high = [1, 80, 120]# 速度范圍(一般設為位置范圍的10%-20%)vel_low = -1vel_high = 1# 運行PSO算法best_position, best_value = pso_algorithm(num_particles, num_dimensions, max_iter, pos_low, pos_high, vel_low, vel_high)# 輸出結果print("\n優化結束!")print(f"找到的最優位置:x1={best_position[0]:.4f}, x2={best_position[1]:.4f}, x3={best_position[2]:.4f}")print(f"對應的最優目標函數值:{best_value:.6f}")
迭代結束后,全局最優位置就是算法找到的 “最佳解”。比如例子中最后得到的最佳位置可能是 (1, 1, 0),對應的函數結果約為 - 10.9048。
四、為什么要用 PSO?
- 簡單好懂:不需要復雜的數學推導,原理像 “群體找東西” 一樣直觀;
- 靈活高效:能解決多變量、復雜函數的優化問題(比如最小化、最大化);
- 容易實現:用 Python 等語言幾行代碼就能寫出基本版本,如上述示例代碼所示。
五、總結
粒子群優化算法(PSO)通過模擬群體中每個個體的 “學習”(參考自己和他人的經驗)來搜索最優解,步驟簡單、效果實用,是解決優化問題的常用工具。就像一群鳥通過互相 “借鑒” 飛行經驗,最終找到食物最多的地方一樣。
上述代碼實現了粒子群優化算法,用于尋找目標函數f(x) = -10x1 - e^(-x2/10 - x3)
的最小值(其中 x1、x2、x3 有明確的范圍限制)。通過運行代碼,可以直觀看到粒子群算法如何通過群體協作逐步逼近最優解,適合零基礎小白理解 PSO 的核心邏輯。如果想解決其他優化問題,只需修改 fitness 函數(目標函數)和 pos_low/pos_high(變量范圍)即可。