数学建模优秀论文算法-差分进化法

由该工具创作数学建模学习平台

差分进化法(Differential Evolution, DE)小白入门教程

一、背景溯源:从进化算法到差分进化

要理解差分进化,先得从进化算法 (Evolutionary Algorithms, EAs)说起------这是一类模拟生物进化规律的启发式算法,核心逻辑是:用"种群"(一组可能的解)通过"变异-交叉-选择"的迭代,像生物进化一样"淘汰差的、保留好的",最终找到最优解

早期的进化算法以**遗传算法(Genetic Algorithm, GA)**为代表,它把解编码成二进制串(比如"0101"代表一个解),通过"交叉"(交换两个二进制串的部分基因)和"变异"(翻转某个二进制位)生成新解。但GA有个明显的缺点:二进制编码会损失精度(比如用8位二进制表示0-255的实数,精度只有1/255),而且编码/解码过程麻烦。

1995年,美国学者Kenneth PriceRainer Storn 为了解决实数域连续优化问题 (比如"调整无人机的3个控制参数让航程最长""优化5个化工参数让产量最高"),提出了差分进化法 。它直接用实数向量表示解(不需要编码),用**"差分向量"**(两个个体的差)代替GA的二进制变异,不仅保留了进化算法的鲁棒性,还大大简化了操作,成为连续优化领域的"爆款算法"。

二、核心思想:用"差异"引导进化

差分进化的核心可以用一个比喻理解:假设你和一群人在森林里找"宝藏"(最优解),每个人的位置代表一个"候选解"。怎么找到宝藏?

  • 你不会瞎逛------而是看别人的位置差:比如看到甲在你东边100米,乙在你西边50米,你会想"甲和乙的位置差是150米东",于是你往东边走一段(比如走50米,即"变异因子"F=50/150=0.33);
  • 你不会完全照搬别人的路线------而是和别人交换信息:比如你和丙交换"东边有没有河""北边有没有树"的信息(交叉操作);
  • 你只会保留更好的选择------比如走完之后发现新位置比原来近,就留在新位置;否则回到原位置(选择操作)。

总结差分进化的核心思想

用"种群中个体的差异"(差分向量)作为"变异的方向和步长",通过"变异-交叉-选择"的循环,让种群一步步向最优解进化。

三、基础概念铺垫:先搞懂这些术语

在正式讲算法前,先明确几个关键术语(用"找宝藏"的例子对应):

  1. 种群(Population) :一群找宝藏的人,对应一组候选解 ,用集合表示为 P = { x 1 , x ∗ 2 , . . . , x ∗ N p } P = \{\mathbf{x}_1, \mathbf{x}*2, ..., \mathbf{x}*{N_p}\} P={x1,x∗2,...,x∗Np},其中 N p N_p Np 是种群大小(比如10个人)。
  2. 个体(Individual) :一个找宝藏的人,对应一个候选解 ,用D维实数向量 表示: x ∗ i = [ x ∗ i , 1 , x i , 2 , . . . , x i , D ] \mathbf{x}*i = [x*{i,1}, x_{i,2}, ..., x_{i,D}] x∗i=[x∗i,1,xi,2,...,xi,D]。其中:
    • D D D 是问题维度(比如优化"无人机高度+速度"两个参数,D=2);
    • x i , j x_{i,j} xi,j 是第i个个体的第j个维度的值(比如"高度=100米"对应j=1的值为100)。
  3. 适应度(Fitness) :这个人离宝藏的距离,对应解的优劣程度 。对于最小化问题 (比如"距离越小越好"),适应度就是目标函数值 f ( x i ) f(\mathbf{x}_i) f(xi);对于最大化问题 (比如"产量越高越好"),适应度可以取 − f ( x i ) -f(\mathbf{x}_i) −f(xi) 或 1 / f ( x i ) 1/f(\mathbf{x}_i) 1/f(xi)(把最大化转成最小化)。
  4. 变量边界 :每个人的位置不能超出森林范围,对应解的可行域 。比如高度不能低于0米、不超过500米,即 x m i n , 1 = 0 x_{min,1}=0 xmin,1=0, x m a x , 1 = 500 x_{max,1}=500 xmax,1=500;速度不能低于10m/s、不超过200m/s,即 x m i n , 2 = 10 x_{min,2}=10 xmin,2=10, x m a x , 2 = 200 x_{max,2}=200 xmax,2=200。所有维度的边界合并为: x ∗ m i n = [ x ∗ m i n , 1 , . . . , x m i n , D ] \mathbf{x}*{min} = [x*{min,1}, ..., x_{min,D}] x∗min=[x∗min,1,...,xmin,D], x ∗ m a x = [ x ∗ m a x , 1 , . . . , x m a x , D ] \mathbf{x}*{max} = [x*{max,1}, ..., x_{max,D}] x∗max=[x∗max,1,...,xmax,D]。

四、算法原理深度拆解:变异-交叉-选择的底层逻辑

差分进化的迭代过程可以概括为"初始化种群 → 变异 → 交叉 → 选择 → 重复直到收敛 "。下面逐个拆解每一步的目的、操作和公式

1. 第一步:初始化种群------随机撒"找宝藏的人"

目的 :生成初始的候选解集合,让种群覆盖整个可行域。操作 :随机生成 N p N_p Np 个D维向量,每个维度的值都在变量边界内。公式如下: x ∗ i ( 0 ) = x ∗ m i n + rand ( 0 , 1 ) D ⋅ ( x ∗ m a x − x ∗ m i n ) ( i = 1 , 2 , . . . , N p ) \mathbf{x}*i^{(0)} = \mathbf{x}*{min} + \text{rand}(0,1)^D \cdot (\mathbf{x}*{max} - \mathbf{x}*{min}) \quad (i=1,2,...,N_p) x∗i(0)=x∗min+rand(0,1)D⋅(x∗max−x∗min)(i=1,2,...,Np)

  • x i ( 0 ) \mathbf{x}_i^{(0)} xi(0):第0代(初始代)的第i个个体;
  • rand ( 0 , 1 ) D \text{rand}(0,1)^D rand(0,1)D:生成一个D维向量,每个元素是0到1的随机数;
  • x ∗ m a x − x ∗ m i n \mathbf{x}*{max} - \mathbf{x}*{min} x∗max−x∗min:可行域的"跨度"(比如高度跨度500米,速度跨度190m/s)。

例子 :假设D=2(高度+速度), x ∗ m i n = [ 0 , 10 ] \mathbf{x}*{min}=[0,10] x∗min=[0,10], x ∗ m a x = [ 500 , 200 ] \mathbf{x}*{max}=[500,200] x∗max=[500,200],生成一个个体: rand ( 0 , 1 ) 2 = [ 0.3 , 0.6 ] \text{rand}(0,1)^2 = [0.3, 0.6] rand(0,1)2=[0.3,0.6],则 x 1 ( 0 ) = [ 0 + 0.3 × 500 , 10 + 0.6 × 190 ] = [ 150 , 124 ] \mathbf{x}_1^{(0)} = [0 + 0.3×500, 10 + 0.6×190] = [150, 124] x1(0)=[0+0.3×500,10+0.6×190]=[150,124](高度150米,速度124m/s)。

2. 第二步:变异------用"别人的差异"调整自己的位置

变异是差分进化的核心 ,它决定了种群进化的方向。目的 :通过"参考其他个体的位置差",让当前个体"向更优的方向移动"。操作逻辑 :从种群中选3个不同的随机个体 (比如甲、乙、丙),用"甲的位置 + 乙和丙的位置差 × 变异因子"生成变异向量(新的候选位置)。

(1)基础变异策略:DE/rand/1(最常用)

公式: v ∗ i ( t ) = x ∗ r 1 ( t ) + F ⋅ ( x ∗ r 2 ( t ) − x ∗ r 3 ( t ) ) \mathbf{v}*i^{(t)} = \mathbf{x}*{r1}^{(t)} + F \cdot (\mathbf{x}*{r2}^{(t)} - \mathbf{x}*{r3}^{(t)}) v∗i(t)=x∗r1(t)+F⋅(x∗r2(t)−x∗r3(t))

符号解释:

  • v i ( t ) \mathbf{v}_i^{(t)} vi(t):第t代第i个个体的变异向量(新的候选位置);
  • x ∗ r 1 , x ∗ r 2 , x r 3 \mathbf{x}*{r1}, \mathbf{x}*{r2}, \mathbf{x}_{r3} x∗r1,x∗r2,xr3:从种群中随机选的3个不同个体(r1≠r2≠r3≠i);
  • F F F:变异因子 (步长),控制"位置差的放大倍数",一般取 0.4 ≤ F ≤ 0.8 0.4 \leq F \leq 0.8 0.4≤F≤0.8。

比喻理解:比如你是第i个人,看到甲在东边,乙在甲东边100米,丙在甲西边50米------乙和丙的差是"东边150米",你取F=0.5,就往东边走75米(甲的位置 + 150×0.5)。

(2)其他常见变异策略

为了平衡"收敛速度"和"种群多样性"(避免过早陷入局部最优),DE还有多种变异策略:

  • DE/best/1 (快收敛,但易早熟):用当前最好个体 代替随机个体r1,直接向最优方向移动: v ∗ i ( t ) = x ∗ b e s t ( t ) + F ⋅ ( x ∗ r 2 ( t ) − x ∗ r 3 ( t ) ) \mathbf{v}*i^{(t)} = \mathbf{x}*{best}^{(t)} + F \cdot (\mathbf{x}*{r2}^{(t)} - \mathbf{x}*{r3}^{(t)}) v∗i(t)=x∗best(t)+F⋅(x∗r2(t)−x∗r3(t))其中 x b e s t ( t ) \mathbf{x}_{best}^{(t)} xbest(t) 是第t代适应度最好的个体(离宝藏最近的人)。
  • DE/rand-to-best/1 (平衡型):既向最好个体靠近,又保留随机差异: v ∗ i ( t ) = x ∗ i ( t ) + F ⋅ ( x ∗ b e s t ( t ) − x ∗ i ( t ) ) + F ⋅ ( x ∗ r 1 ( t ) − x ∗ r 2 ( t ) ) \mathbf{v}*i^{(t)} = \mathbf{x}*i^{(t)} + F \cdot (\mathbf{x}*{best}^{(t)} - \mathbf{x}*i^{(t)}) + F \cdot (\mathbf{x}*{r1}^{(t)} - \mathbf{x}*{r2}^{(t)}) v∗i(t)=x∗i(t)+F⋅(x∗best(t)−x∗i(t))+F⋅(x∗r1(t)−x∗r2(t))

3. 第三步:交叉------交换信息,保持多样性

变异后的向量可能"太激进"(比如走了太远),交叉的作用是把原个体和变异向量的信息结合 ,避免种群"同质化"。目的 :让新解同时保留"原个体的优点"和"变异向量的新信息"。操作逻辑 :对每个维度j(比如高度、速度),用交叉概率 Cr决定"取原个体的值还是变异向量的值",同时强制至少有一个维度取变异向量的值(避免交叉后和原个体完全一样)。

公式: u i , j ( t ) = { v i , j ( t ) , 如果 rand ( 0 , 1 ) ≤ C r 或 j = j r a n d x i , j ( t ) , 否则 u_{i,j}^{(t)} = \begin{cases} v_{i,j}^{(t)}, & \text{如果} \ \text{rand}(0,1) \leq Cr \ 或 \ j = j_{rand} \\ x_{i,j}^{(t)}, & \text{否则} \end{cases} ui,j(t)={vi,j(t),xi,j(t),如果 rand(0,1)≤Cr 或 j=jrand否则

符号解释:

  • u ∗ i ( t ) = [ u ∗ i , 1 , . . . , u i , D ] \mathbf{u}*i^{(t)} = [u*{i,1}, ..., u_{i,D}] u∗i(t)=[u∗i,1,...,ui,D]:第t代第i个个体的试验向量(交叉后的新解);
  • C r Cr Cr:交叉概率 ,控制"取变异向量值的概率",一般取 0.1 ≤ C r ≤ 0.9 0.1 \leq Cr \leq 0.9 0.1≤Cr≤0.9;
  • j r a n d j_{rand} jrand:从1到D中随机选的一个维度(强制至少有一个维度取变异向量的值);
  • rand ( 0 , 1 ) \text{rand}(0,1) rand(0,1):0到1的均匀随机数。

例子:假设你是第i个人,原位置是[150, 124](高度150,速度124),变异向量是[200, 150](高度200,速度150),Cr=0.7,j_rand=2:

  • 对j=1(高度):rand(0,1)=0.6 ≤ 0.7 → 取变异向量的值200;
  • 对j=2(速度):因为j=j_rand → 强制取变异向量的值150;
  • 试验向量就是[200, 150]。

4. 第四步:选择------只保留更好的解

选择是进化的"方向舵",它保证种群只会往"更好"的方向发展。目的 :淘汰差的解,保留好的解,让种群逐步优化。操作逻辑 :用贪心策略 ------比较原个体 x i ( t ) \mathbf{x}_i^{(t)} xi(t) 和试验向量 u i ( t ) \mathbf{u}_i^{(t)} ui(t) 的适应度,保留适应度更好的那个作为下一代的个体。

公式(以最小化问题 为例,即适应度越小越好): x i ( t + 1 ) = { u i ( t ) , 如果 f ( u i ( t ) ) ≤ f ( x i ( t ) ) x i ( t ) , 否则 \mathbf{x}_i^{(t+1)} = \begin{cases} \mathbf{u}_i^{(t)}, & \text{如果} \ f(\mathbf{u}_i^{(t)}) \leq f(\mathbf{x}_i^{(t)}) \\ \mathbf{x}_i^{(t)}, & \text{否则} \end{cases} xi(t+1)={ui(t),xi(t),如果 f(ui(t))≤f(xi(t))否则

符号解释:

  • x i ( t + 1 ) \mathbf{x}_i^{(t+1)} xi(t+1):第t+1代第i个个体(下一代的位置);
  • f ( ⋅ ) f(\cdot) f(⋅):目标函数(比如"距离宝藏的距离")。

例子:原个体的适应度是f([150,124])=100(距离100米),试验向量的适应度是f([200,150])=80(距离80米)→ 下一代保留试验向量[200,150]。

五、完整算法流程:一步一步走

现在把前面的步骤串起来,给出差分进化的完整迭代流程(以最基础的DE/rand/1策略为例):

1. 步骤0:设定参数与初始化

首先明确问题的输入

  • 目标函数: min ⁡ f ( x ) \min f(\mathbf{x}) minf(x)(或 max ⁡ f ( x ) \max f(\mathbf{x}) maxf(x));
  • 变量边界: x ∗ m i n \mathbf{x}*{min} x∗min(下限)、 x ∗ m a x \mathbf{x}*{max} x∗max(上限);
  • 算法参数:种群大小 N p N_p Np(一般取 5 D ≤ N p ≤ 10 D 5D \leq N_p \leq 10D 5D≤Np≤10D)、变异因子F(0.4-0.8)、交叉概率Cr(0.1-0.9)、最大迭代次数 G m a x G_{max} Gmax(一般取100-1000)。

然后初始化种群 :用第一节的公式生成第0代的Np个个体 x i ( 0 ) \mathbf{x}_i^{(0)} xi(0)(i=1到Np)。

2. 步骤1:计算初始适应度

对每个初始个体 x i ( 0 ) \mathbf{x}_i^{(0)} xi(0),计算其适应度 f ( x ∗ i ( 0 ) ) f(\mathbf{x}*i^{(0)}) f(x∗i(0)),并记录当前最好个体 x ∗ b e s t ( 0 ) \mathbf{x}*{best}^{(0)} x∗best(0)(适应度最小的那个)。

3. 步骤2:迭代进化(t从0到Gmax-1)

循环执行"变异→交叉→选择",直到达到最大迭代次数:

(1)变异:生成所有个体的变异向量

对每个个体i(1到Np):

  • 随机选3个不同的个体 x ∗ r 1 , x ∗ r 2 , x r 3 \mathbf{x}*{r1}, \mathbf{x}*{r2}, \mathbf{x}_{r3} x∗r1,x∗r2,xr3(r1≠r2≠r3≠i);
  • 用DE/rand/1策略生成变异向量 v i ( t ) \mathbf{v}_i^{(t)} vi(t)。
(2)交叉:生成所有个体的试验向量

对每个个体i(1到Np):

  • 随机选一个维度 j r a n d j_{rand} jrand(1到D);
  • 对每个维度j(1到D):
    • 如果rand(0,1) ≤ Cr 或 j=j_rand → 取变异向量的值 v i , j ( t ) v_{i,j}^{(t)} vi,j(t);
    • 否则 → 取原个体的值 x i , j ( t ) x_{i,j}^{(t)} xi,j(t);
  • 得到试验向量 u i ( t ) \mathbf{u}_i^{(t)} ui(t)。
(3)选择:更新下一代种群

对每个个体i(1到Np):

  • 计算试验向量的适应度 f ( u i ( t ) ) f(\mathbf{u}_i^{(t)}) f(ui(t));
  • 比较 f ( u i ( t ) ) f(\mathbf{u}_i^{(t)}) f(ui(t)) 和原个体的适应度 f ( x i ( t ) ) f(\mathbf{x}_i^{(t)}) f(xi(t)):
    • 如果试验向量更好 → 下一代个体 x i ( t + 1 ) = u i ( t ) \mathbf{x}_i^{(t+1)} = \mathbf{u}_i^{(t)} xi(t+1)=ui(t);
    • 否则 → 下一代个体 x i ( t + 1 ) = x i ( t ) \mathbf{x}_i^{(t+1)} = \mathbf{x}_i^{(t)} xi(t+1)=xi(t)。
(4)更新当前最好个体

从下一代种群 { x ∗ 1 ( t + 1 ) , . . . , x ∗ N p ( t + 1 ) } \{\mathbf{x}*1^{(t+1)}, ..., \mathbf{x}*{N_p}^{(t+1)}\} {x∗1(t+1),...,x∗Np(t+1)} 中找到适应度最小的个体,更新 x b e s t ( t + 1 ) \mathbf{x}_{best}^{(t+1)} xbest(t+1)。

4. 步骤3:输出结果

迭代结束后,输出最终最好个体 x ∗ b e s t ( G m a x ) \mathbf{x}*{best}^{(Gmax)} x∗best(Gmax) 及其适应度 f ( x ∗ b e s t ( G m a x ) ) f(\mathbf{x}*{best}^{(Gmax)}) f(x∗best(Gmax)),这就是DE找到的近似最优解

六、适用边界与参数调优:什么时候用?怎么调?

1. 最适合的问题类型

DE是连续优化问题的"瑞士军刀",最适合以下场景:

  • 实数域连续优化(比如调整神经网络权重、优化化工反应参数、设计飞机翼型);
  • 高维问题(D≥50,比如优化100个变量的函数);
  • 非凸/多峰问题(目标函数有多个局部最优,比如"地形有多个小山峰");
  • 不可微问题(目标函数没有导数,无法用梯度下降等算法)。

2. 不适合的问题类型

  • 离散优化(比如0-1变量、整数变量、旅行商问题TSP):原生DE用实数向量,处理离散问题需要额外编码(比如用实数映射到离散值),效率不如专门的离散算法(如遗传算法);
  • 线性规划(有明确约束的线性问题):有更高效的算法(如单纯形法);
  • 需要精确解的问题 :DE是启发式算法,只能找到近似最优解,不适合需要精确解的场景(比如数学证明)。

3. 参数调优技巧(小白必看)

DE的性能高度依赖3个核心参数(Np、F、Cr),以下是经验法则:

  • 种群大小Np

    • 太小(比如Np<5D):种群多样性不足,容易早熟;
    • 太大(比如Np>20D):计算量太大,收敛慢;
    • 推荐: N p = 5 D ∼ 10 D Np = 5D \sim 10D Np=5D∼10D(比如D=10 → Np=50~100)。
  • 变异因子F

    • 太小(F<0.4):变异步长太小,收敛慢;
    • 太大(F>0.8):变异步长太大,容易跳过最优解;
    • 推荐:固定F=0.6,或用自适应F(比如每代随机选0.5~0.9的F)。
  • 交叉概率Cr

    • 太小(Cr<0.3):交叉后和原个体太像,变异效果弱;
    • 太大(Cr>0.8):交叉后破坏原个体的好特征,容易不稳定;
    • 推荐:固定Cr=0.7,或对高维问题 取大Cr(比如Cr=0.9),对低维问题取小Cr(比如Cr=0.5)。

七、总结:差分进化的"优"与"缺"

优点

  1. 简单易实现:只有"变异-交叉-选择"三步,不需要复杂的数学推导;
  2. 鲁棒性强:对初始种群不敏感,即使初始解很差,也能逐步优化;
  3. 通用性好:不需要目标函数的导数,适合各种连续优化问题;
  4. 高维表现好:在高维问题(D=100+)上的性能优于很多进化算法。

缺点

  1. 离散问题需改进:原生DE不支持离散变量,处理离散问题需要额外编码;
  2. 参数敏感:参数调优需要经验,新手可能需要多次尝试;
  3. 收敛速度慢:相比梯度下降等算法,DE的收敛速度较慢(但梯度下降需要导数)。

八、小试牛刀:用DE优化一个简单函数

最后用一个极简例子 帮你巩固流程:问题 :最小化函数 f ( x , y ) = x 2 + y 2 f(x,y) = x^2 + y^2 f(x,y)=x2+y2(最优解是(0,0),适应度0)。参数:Np=10,F=0.5,Cr=0.7,Gmax=50,变量边界x∈[-5,5],y∈[-5,5]。

步骤1:初始化种群

生成10个2维向量,比如其中一个是[1.2, -0.8](x=1.2,y=-0.8),适应度是1.2²+(-0.8)²=2.08。

步骤2:变异(DE/rand/1)

选3个随机个体[2.3, 1.5]、[-3.4, 2.1]、[0.5, -1.3],生成变异向量:$

\mathbf{v} = [2.3] + 0.5×([-3.4]-[0.5], [2.1]-[-1.3]) = [2.3 - 1.95, 1.5 + 1.7] = [0.35, 3.2]

$

步骤3:交叉

原个体是[1.2, -0.8],Cr=0.7,j_rand=2:

  • j=1:rand(0,1)=0.6 ≤0.7 → 取变异向量的0.35;
  • j=2:j=j_rand → 取变异向量的3.2;
  • 试验向量是[0.35, 3.2],适应度是0.35²+3.2²=0.1225+10.24=10.3625(比原个体的2.08差,所以选择原个体)。

步骤4:迭代50次

经过50次迭代后,最好的个体可能是[0.01, -0.02],适应度≈0.0005,非常接近最优解(0,0)。

九、结语:差分进化的"小白哲学"

差分进化的本质是**"简单的力量"**------没有复杂的编码,没有高深的数学,只用"个体的差异"引导进化,却能解决很多复杂问题。

对小白来说,学习DE的关键不是记住公式,而是理解**"变异用差异、交叉保多样、选择定方向"**的核心逻辑。当你遇到"不知道怎么求导""变量太多""函数有多个峰"的问题时,不妨试试DE------它可能不是最完美的,但一定是最"接地气"的进化算法。

现在,你已经掌握了DE的全部核心知识,接下来可以找一个简单的连续优化问题(比如优化 f ( x ) = x 2 + 2 x + 1 f(x) = x^2 + 2x + 1 f(x)=x2+2x+1),按照流程手动走一遍------实践是最好的老师

案例介绍

最小化二维函数 ( f(x,y) = x^2 + y^2 ),最优解为 (0,0),适应度为 0。参数设置:种群大小Np=10,变异因子F=0.5,交叉概率Cr=0.7,最大迭代次数Gmax=50,变量边界x∈[-5,5],y∈[-5,5]。

python 复制代码
import random
import numpy as np

def init_population(Np, D, x_min, x_max):
    """
    初始化种群
    :param Np: 种群大小
    :param D: 问题维度
    :param x_min: 变量下限向量
    :param x_max: 变量上限向量
    :return: 初始种群,shape=(Np, D)
    """
    population = np.zeros((Np, D))
    for i in range(Np):
        # 随机生成每个维度的初始值,在[x_min, x_max]范围内
        population[i, :] = x_min + np.random.rand(D) * (x_max - x_min)
    return population

def evaluate_fitness(population, objective_func):
    """
    计算种群中每个个体的适应度
    :param population: 种群矩阵,shape=(Np, D)
    :param objective_func: 目标函数
    :return: 适应度向量,shape=(Np,)
    """
    Np = population.shape[0]
    fitness = np.zeros(Np)
    for i in range(Np):
        fitness[i] = objective_func(population[i, :])
    return fitness

def select_best_individual(population, fitness):
    """
    选择种群中的最优个体
    :param population: 种群矩阵
    :param fitness: 适应度向量
    :return: 最优个体向量,最优适应度值
    """
    best_idx = np.argmin(fitness)  # 最小化问题,取适应度最小的索引
    best_individual = population[best_idx, :].copy()
    best_fitness = fitness[best_idx]
    return best_individual, best_fitness

def mutate(population, F):
    """
    变异操作,采用DE/rand/1策略
    :param population: 种群矩阵
    :param F: 变异因子
    :return: 变异向量矩阵,shape=(Np, D)
    """
    Np, D = population.shape
    mutated = np.zeros_like(population)
    for i in range(Np):
        # 随机选择3个不同的个体,且不等于当前个体i
        r1 = r2 = r3 = i
        while r1 == i:
            r1 = random.randint(0, Np - 1)
        while r2 == i or r2 == r1:
            r2 = random.randint(0, Np - 1)
        while r3 == i or r3 == r1 or r3 == r2:
            r3 = random.randint(0, Np - 1)
        # 计算变异向量
        mutated[i, :] = population[r1, :] + F * (population[r2, :] - population[r3, :])
    return mutated

def crossover(population, mutated, Cr):
    """
    交叉操作,采用二项式交叉
    :param population: 原种群矩阵
    :param mutated: 变异向量矩阵
    :param Cr: 交叉概率
    :return: 试验向量矩阵,shape=(Np, D)
    """
    Np, D = population.shape
    trial = np.zeros_like(population)
    for i in range(Np):
        # 随机选择一个强制交叉的维度
        j_rand = random.randint(0, D - 1)
        for j in range(D):
            # 二项式交叉规则
            if random.random() <= Cr or j == j_rand:
                trial[i, j] = mutated[i, j]
            else:
                trial[i, j] = population[i, j]
    return trial

def select_next_generation(population, trial, fitness_org, fitness_trial):
    """
    选择操作,采用贪心策略生成下一代种群
    :param population: 原种群矩阵
    :param trial: 试验向量矩阵
    :param fitness_org: 原种群适应度向量
    :param fitness_trial: 试验向量适应度向量
    :return: 下一代种群矩阵,下一代种群适应度向量
    """
    Np = population.shape[0]
    next_pop = np.zeros_like(population)
    next_fitness = np.zeros(Np)
    for i in range(Np):
        # 最小化问题,选择适应度更小的个体
        if fitness_trial[i] <= fitness_org[i]:
            next_pop[i, :] = trial[i, :]
            next_fitness[i] = fitness_trial[i]
        else:
            next_pop[i, :] = population[i, :]
            next_fitness[i] = fitness_org[i]
    return next_pop, next_fitness

def differential_evolution(objective_func, D, x_min, x_max, Np, F, Cr, Gmax):
    """
    差分进化算法主函数
    :param objective_func: 目标函数(最小化)
    :param D: 问题维度
    :param x_min: 变量下限向量,shape=(D,)
    :param x_max: 变量上限向量,shape=(D,)
    :param Np: 种群大小
    :param F: 变异因子
    :param Cr: 交叉概率
    :param Gmax: 最大迭代次数
    :return: 最优个体,最优适应度,迭代历史最优适应度
    """
    # 步骤1: 初始化种群
    population = init_population(Np, D, x_min, x_max)
    # 步骤2: 计算初始适应度
    fitness = evaluate_fitness(population, objective_func)
    # 记录初始最优个体
    best_individual, best_fitness = select_best_individual(population, fitness)
    # 存储迭代历史最优适应度
    history_best = [best_fitness]

    # 步骤3: 迭代进化
    for t in range(Gmax):
        # 3.1 变异操作
        mutated = mutate(population, F)
        # 3.2 交叉操作
        trial = crossover(population, mutated, Cr)
        # 3.3 计算试验向量的适应度
        fitness_trial = evaluate_fitness(trial, objective_func)
        # 3.4 选择操作生成下一代
        population, fitness = select_next_generation(population, trial, fitness, fitness_trial)
        # 3.5 更新当前最优个体
        current_best_ind, current_best_fit = select_best_individual(population, fitness)
        if current_best_fit < best_fitness:
            best_fitness = current_best_fit
            best_individual = current_best_ind.copy()
        # 记录历史最优
        history_best.append(best_fitness)

    # 步骤4: 输出结果
    return best_individual, best_fitness, history_best

def objective_func(X):
    """
    目标函数:f(x,y) = x^2 + y^2
    :param X: 输入向量,shape=(2,)
    :return: 函数值
    """
    return X[0] ** 2 + X[1] ** 2

if __name__ == "__main__":
    # 设置问题参数
    D = 2  # 二维问题
    x_min = np.array([-5.0, -5.0])  # 变量下限
    x_max = np.array([5.0, 5.0])  # 变量上限

    # 设置DE算法参数
    Np = 10  # 种群大小
    F = 0.5  # 变异因子
    Cr = 0.7  # 交叉概率
    Gmax = 50  # 最大迭代次数

    # 运行DE算法
    best_ind, best_fit, history = differential_evolution(objective_func, D, x_min, x_max, Np, F, Cr, Gmax)

    # 打印结果
    print("最优个体:", best_ind)
    print("最优适应度:", best_fit)

代码整体定位与算法框架

这段代码实现了差分进化算法(DE, Differential Evolution) 中的 DE/rand/1/bin 策略,用于无约束连续优化问题 ------最小化二维凸函数 ( f(x,y)=x2+y2 )(最优解为 ( (0,0) ),适应度为0)。DE是一种基于群体智能的全局优化算法,核心思想是通过差分变异 产生新解,结合交叉 增加多样性,最后通过贪心选择保留优解。


1. 环境与依赖解析

python 复制代码
import random
import numpy as np
  • random:用于生成离散随机数(如交叉维度、变异个体索引)。
  • numpy:用于高效的向量/矩阵运算(如种群初始化、差分计算),是DE等数值优化算法的必备依赖。

2. 核心子函数解析(数学原理+代码逐行)

2.1 种群初始化 init_population

数学原理 :在可行域内均匀随机抽样生成 ( N_p ) 个个体,每个个体为 ( D ) 维向量,分量满足 ( x_j \in [x_{\text{min}}(j), x_{\text{max}}(j)] )。

python 复制代码
def init_population(Np, D, x_min, x_max):
    # 1. 创建Np行D列的全零矩阵存储种群
    population = np.zeros((Np, D))
    # 2. 遍历每个个体i
    for i in range(Np):
        # 3. 逐分量生成[min, max]内的随机值:x_min + rand(0,1)*(max-min)
        population[i, :] = x_min + np.random.rand(D) * (x_max - x_min)
    return population  # 返回shape=(Np,D)的初始种群
  • np.random.rand(D):生成 ( D ) 个**[0,1)均匀分布**的随机数(向量)。
  • 向量运算高效性:x_min + rand*(x_max-x_min) 是逐分量计算,无需循环遍历维度,这是numpy的优势。

2.2 适应度计算 evaluate_fitness

数学原理 :适应度是个体对环境的"适应程度",对于最小化问题,适应度直接等于目标函数值。

python 复制代码
def evaluate_fitness(population, objective_func):
    Np = population.shape[0]  # 获取种群大小
    fitness = np.zeros(Np)     # 创建适应度向量
    # 遍历每个个体,计算其目标函数值(适应度)
    for i in range(Np):
        fitness[i] = objective_func(population[i, :])
    return fitness  # 返回shape=(Np,)的适应度向量
  • 通用性:通过传入objective_func参数,支持任意目标函数的适配。

2.3 最优个体选择 select_best_individual

数学原理 :在当前种群中,找出适应度最小的个体(最小化问题)。

python 复制代码
def select_best_individual(population, fitness):
    # 1. 找到适应度最小的个体索引(argmin是numpy的最小值索引函数)
    best_idx = np.argmin(fitness)
    # 2. 深拷贝最优个体:避免后续修改种群时影响最优解的存储
    best_individual = population[best_idx, :].copy()
    best_fitness = fitness[best_idx]  # 最优适应度
    return best_individual, best_fitness  # 返回最优个体和适应度
  • 注意事项:使用.copy()是关键,否则best_individual会随种群矩阵的修改而变化(numpy数组默认是浅拷贝)。

2.4 变异操作 mutate(核心!DE/rand/1策略)

数学原理 :采用 DE/rand/1 变异策略,通过3个随机个体的差分 生成变异向量:
v ∗ i = x ∗ r 1 + F ⋅ ( x ∗ r 2 − x ∗ r 3 ) \boldsymbol{v}*i = \boldsymbol{x}*{r_1} + F \cdot (\boldsymbol{x}*{r_2} - \boldsymbol{x}*{r_3}) v∗i=x∗r1+F⋅(x∗r2−x∗r3)

其中:

  • ( r_1, r_2, r_3 ):随机选择的3个互不相同不等于当前个体i的索引(保证变异多样性);
  • ( F \in (0,2) ):变异因子,控制差分步长(本例取0.5,经验值为0.4-0.9)。
python 复制代码
def mutate(population, F):
    Np, D = population.shape  # 获取种群大小和维度
    mutated = np.zeros_like(population)  # 创建与种群同形状的变异矩阵
    # 遍历每个个体i,生成对应的变异向量v_i
    for i in range(Np):
        # 1. 初始化r1/r2/r3为i,然后通过while循环确保:
        #    r1≠i,r2≠i且≠r1,r3≠i且≠r1且≠r2
        r1 = r2 = r3 = i
        while r1 == i: r1 = random.randint(0, Np-1)
        while r2 == i or r2 == r1: r2 = random.randint(0, Np-1)
        while r3 == i or r3 == r1 or r3 == r2: r3 = random.randint(0, Np-1)
        # 2. 按DE/rand/1公式计算变异向量
        mutated[i, :] = population[r1, :] + F * (population[r2, :] - population[r3, :])
    return mutated  # 返回变异向量矩阵
  • 多样性保障:while循环严格确保3个随机个体互不重复且非当前个体,避免变异退化。

2.5 交叉操作 crossover(核心!二项式交叉)

数学原理 :采用 二项式交叉(bin) 策略,将原个体 ( \boldsymbol{x}_i ) 与变异向量 ( \boldsymbol{v}_i ) 按概率混合,生成试验向量 ( \boldsymbol{u}_i )。规则:

  1. 随机选择一个强制交叉维度 ( j_{\text{rand}} )(保证试验向量至少有一个维度与原个体不同);
  2. 对其他维度 ( j ),若 ( \text{rand}(0,1) \leq Cr )(交叉概率)或 ( j = j_{\text{rand}} ),则 ( u_{ij} = v_{ij} ),否则 ( u_{ij} = x_{ij} )。
python 复制代码
def crossover(population, mutated, Cr):
    Np, D = population.shape  # 获取种群大小和维度
    trial = np.zeros_like(population)  # 创建试验向量矩阵
    # 遍历每个个体i,生成对应的试验向量u_i
    for i in range(Np):
        # 1. 随机选择一个强制交叉的维度
        j_rand = random.randint(0, D-1)
        # 2. 遍历每个维度j,按二项式交叉规则混合
        for j in range(D):
            if random.random() <= Cr or j == j_rand:
                trial[i, j] = mutated[i, j]  # 用变异向量的分量
            else:
                trial[i, j] = population[i, j]  # 用原个体的分量
    return trial  # 返回试验向量矩阵
  • 关键设计:强制交叉维度 ( j_{\text{rand}} ) 避免了试验向量与原个体完全一致的情况,确保了变异的有效性。

2.6 下一代选择 select_next_generation(贪心选择)

数学原理 :采用贪心策略 生成下一代种群------对每个个体 ( i ),比较原个体 ( \boldsymbol{x}_i ) 和试验向量 ( \boldsymbol{u}_i ) 的适应度,选择适应度更小 的个体进入下一代:
x i ( g + 1 ) = { u i if f ( u i ) ≤ f ( x i ) x i otherwise \boldsymbol{x}_i^{(g+1)} = \begin{cases} \boldsymbol{u}_i & \text{if } f(\boldsymbol{u}_i) \leq f(\boldsymbol{x}_i) \\ \boldsymbol{x}_i & \text{otherwise} \end{cases} xi(g+1)={uixiif f(ui)≤f(xi)otherwise

python 复制代码
def select_next_generation(population, trial, fitness_org, fitness_trial):
    Np = population.shape[0]
    next_pop = np.zeros_like(population)     # 下一代种群矩阵
    next_fitness = np.zeros(Np)              # 下一代适应度向量
    # 遍历每个个体i,贪心选择更优个体
    for i in range(Np):
        if fitness_trial[i] <= fitness_org[i]:
            next_pop[i, :] = trial[i, :]
            next_fitness[i] = fitness_trial[i]
        else:
            next_pop[i, :] = population[i, :]
            next_fitness[i] = fitness_org[i]
    return next_pop, next_fitness  # 返回下一代种群和适应度
  • 收敛性保障:贪心选择确保种群的最优适应度不会随迭代变差,这是DE收敛的重要保证。

2.7 DE主函数 differential_evolution

数学流程:完整实现DE的迭代进化逻辑,包含以下核心步骤:

  1. 初始化种群 → 2. 计算初始适应度 → 3. 记录初始最优 → 4. 迭代进化(变异→交叉→适应度→选择→更新最优)→ 5. 输出结果。
python 复制代码
def differential_evolution(objective_func, D, x_min, x_max, Np, F, Cr, Gmax):
    # 步骤1: 初始化种群
    population = init_population(Np, D, x_min, x_max)
    # 步骤2: 计算初始种群的适应度
    fitness = evaluate_fitness(population, objective_func)
    # 步骤3: 记录初始最优个体和适应度
    best_individual, best_fitness = select_best_individual(population, fitness)
    history_best = [best_fitness]  # 存储迭代历史最优适应度(用于收敛性分析)

    # 步骤4: 迭代进化(共Gmax次)
    for t in range(Gmax):
        # 4.1 变异操作:生成变异向量矩阵
        mutated = mutate(population, F)
        # 4.2 交叉操作:生成试验向量矩阵
        trial = crossover(population, mutated, Cr)
        # 4.3 计算试验向量的适应度
        fitness_trial = evaluate_fitness(trial, objective_func)
        # 4.4 贪心选择:生成下一代种群
        population, fitness = select_next_generation(population, trial, fitness, fitness_trial)
        # 4.5 更新全局最优个体和适应度
        current_best_ind, current_best_fit = select_best_individual(population, fitness)
        if current_best_fit < best_fitness:  # 仅当当前最优更优时更新全局最优
            best_fitness = current_best_fit
            best_individual = current_best_ind.copy()  # 深拷贝避免后续修改
        # 4.6 记录当前迭代的全局最优适应度
        history_best.append(best_fitness)

    # 步骤5: 输出结果
    return best_individual, best_fitness, history_best

2.8 目标函数 objective_func

数学定义 :本例优化的二维函数 ( f(x,y)=x2+y2 ),是典型的无约束凸函数,全局最优解为 ( (0,0) ),适应度为0。

python 复制代码
def objective_func(X):
    # X是shape=(D,)的numpy数组,X[0]是x,X[1]是y
    return X[0] ** 2 + X[1] ** 2  # 返回目标函数值(适应度)

3. 主运行模块解析(参数设置与结果输出)

python 复制代码
if __name__ == "__main__":
    # ------------------------- 问题参数设置 -------------------------
    D = 2                         # 问题维度(二维:x,y)
    x_min = np.array([-5.0, -5.0])  # 变量下限向量:x∈[-5,5], y∈[-5,5]
    x_max = np.array([5.0, 5.0])     # 变量上限向量

    # ------------------------- DE算法参数设置 -------------------------
    Np = 10     # 种群大小(经验值:10~100,维度高时增大)
    F = 0.5     # 变异因子(经验值:0.4~0.9)
    Cr = 0.7    # 交叉概率(经验值:0.5~0.95)
    Gmax = 50   # 最大迭代次数(经验值:50~500)

    # ------------------------- 运行DE算法 -------------------------
    best_ind, best_fit, history = differential_evolution(
        objective_func, D, x_min, x_max, Np, F, Cr, Gmax)

    # ------------------------- 输出结果 -------------------------
    print("最优个体:", best_ind)      # 接近(0,0)的向量
    print("最优适应度:", best_fit)    # 接近0的数值

4. 代码运行逻辑与预期结果

4.1 运行逻辑链

复制代码
初始化种群 → 计算初始适应度 → 记录初始最优 →
迭代1: 变异 → 交叉 → 试验适应度 → 贪心选择 → 更新最优 → 记录历史最优 →
迭代2: 重复上述步骤 →
...
迭代Gmax: 重复上述步骤 → 输出最终最优解

4.2 预期结果

由于目标函数是凸函数且DE参数合理,运行后会得到:

  • 最优个体:接近 [0.0, 0.0] 的numpy数组(如 [-1.2e-6, 8.5e-7]);
  • 最优适应度:接近 0 的极小值(如 1.9e-12);
  • 历史最优 history:是一个从初始适应度(约25左右,因为初始个体在[-5,5]均匀分布,x²+y²的均值约为25)逐渐下降到接近0的列表,体现收敛过程。

5. 代码优缺点与建模优化建议

5.1 优点

  • 结构清晰:模块化设计,每个子函数对应DE的一个核心步骤,易于理解和修改;
  • 通用性强:通过传入目标函数参数,支持任意维度和目标函数的优化;
  • 实现规范:严格遵循DE/rand/1/bin的标准数学定义,结果可靠。

5.2 待优化点(建模比赛加分项)

  1. 边界处理 :当前代码未限制变异/交叉后的向量在可行域内,若目标函数在边界外有极值,会导致错误。建议在变异/交叉后加入截断处理

    python 复制代码
    # 变异后截断边界(可添加到mutate或crossover函数末尾)
    mutated = np.clip(mutated, x_min, x_max)
  2. 自适应参数 :固定的 ( F ) 和 ( Cr ) 可能限制收敛速度。建议采用自适应策略,如随迭代次数动态调整:

    • ( F ):在 0.2~1.0 间按适应度分散度自适应;
    • ( Cr ):在 0.5~0.95 间随迭代次数递增。
  3. 并行化计算 :当种群较大或目标函数复杂时,可将适应度计算改为多进程并行,利用多核CPU加速。

  4. 终止条件 :除了最大迭代次数,可添加适应度变化阈值作为提前终止条件(如连续10次迭代最优适应度变化小于1e-8,则终止)。


6. 代码在数学建模中的应用场景

这段代码可直接用于:

  • 连续优化问题(如参数拟合、资源分配、工程设计等);
  • 凸/非凸函数优化(DE对非凸函数的全局寻优能力较强);
  • 高维问题 (只需修改 Dx_min/x_max 即可适配)。

在建模比赛中,可将目标函数替换为比赛问题的数学模型,调整DE参数后即可快速得到最优解。


附录:DE/rand/1/bin策略的数学符号定义

符号 含义 代码对应
\( N_p \) 种群大小 `Np`
\( D \) 问题维度 `D`
\( \boldsymbol{x}_i \) 第i个个体 `population[i,:]`
\( F \) 变异因子 `F`
\( Cr \) 交叉概率 `Cr`
\( G_{\text{max}} \) 最大迭代次数 `Gmax`
\( \boldsymbol{v}_i \) 变异向量 `mutated[i,:]`
\( \boldsymbol{u}_i \) 试验向量 `trial[i,:]`
\( f(\cdot) \) 目标函数 `objective_func`
相关推荐
C灿灿数模3 小时前
2025未来杯数学建模A题B题思路模型代码论文(开赛后持续更新)
数学建模
人大博士的交易之路5 小时前
龙虎榜——20251204
数学建模·数据挖掘·数据分析·缠论·龙虎榜·道琼斯结构
Deepoch20 小时前
Deepoc-M 破局:半导体研发告别试错内耗
大数据·人工智能·数学建模·半导体·具身模型·deepoc
人大博士的交易之路1 天前
龙虎榜——20251203
数学建模·数据挖掘·数据分析·缠论·龙虎榜·道琼斯结构
智算菩萨1 天前
【3D建模】人体投弹动作的3D建模与实时动作演示系统
数学建模·3d·动画
人大博士的交易之路1 天前
今日行情明日机会——20251203
数学建模·数据挖掘·数据分析·缠论·道琼斯结构
业精于勤的牙2 天前
SU草图大师 | SketchUp 2025百度云盘官方正式版下载
数学建模·sketch
泰迪智能科技2 天前
分享高校数学建模实验室项目资源+实训软件+产融服务
数学建模
Cathy Bryant2 天前
信息论(11):链式法则-证明
笔记·算法·数学建模·概率论·信息与通信