关键词

差分进化DE/rand/1DE/best/1变异操作交叉操作
F缩放因子CR交叉率二项式交叉指数交叉选择操作
约束处理自适应参数边界约束罚函数成功历史
jDESaDESHADEL-SHADE多目标DE

摘要

差分进化(Differential Evolution, DE)是由Rainer Storn和Kenneth Price于1995年提出的高效全局优化算法,专门用于解决实数参数空间的连续优化问题。DE以其简洁的算法结构、较少的控制参数和卓越的优化性能著称,在多个基准测试和实际应用中获得优异表现。与传统遗传算法的随机变异不同,DE采用基于差向量的变异策略,使其在局部搜索和全局探索之间实现了良好的平衡。


一、差分进化的基本原理

1.1 算法起源与动机

1995年,Storn和Price在解决切比雪夫多项式拟合问题时提出了差分进化算法。该算法的核心创新在于利用种群中个体间的差分向量来引导搜索方向,这一设计灵感来源于自然界中生物群体协同进化的特性。

DE的核心思想

其中为三个互不相同的随机索引,为缩放因子。

1.2 算法框架概览

┌─────────────────────────────────────────────────────┐
│                   差分进化流程                        │
├─────────────────────────────────────────────────────┤
│  1. 初始化种群 X⁰ = {x¹, x², ..., xᴺᴾ}              │
│  2. FOR g = 1 TO G_max:                             │
│      FOR i = 1 TO Nᴾ:                              │
│          │ 2.1 变异: 生成变异向量 vⁱ                   │
│          │ 2.2 交叉: 生成试验向量 uⁱ                   │
│          │ 2.3 选择: 基于适应度决定存活者                │
│      END FOR                                       │
│  3. 输出最优解                                      │
└─────────────────────────────────────────────────────┘

1.3 与遗传算法的关键区别

特征遗传算法差分进化
变异机制随机扰动差分向量驱动
搜索方向无方向性沿梯度方向
参数敏感性中等较低
收敛速度较慢较快
全局搜索中等(依赖参数)

二、核心操作详解

2.1 初始化

DE在维搜索空间内随机初始化种群:

def initialize_population(NP, D, bounds):
    """
    NP: 种群规模
    D: 维度
    bounds: [(low1, high1), ..., (lowD, highD)]
    """
    population = []
    for i in range(NP):
        individual = np.array([np.random.uniform(low, high) 
                              for low, high in bounds])
        population.append(individual)
    return np.array(population)

边界处理:初始化后需确保所有个体满足边界约束:

2.2 变异操作

2.2.1 基本变异策略

DE/rand/1(最常用,最稳定):

def de_rand_1(population, F=0.5, i=None):
    """
    随机选择三个不同于i的个体
    """
    NP = len(population)
    candidates = [j for j in range(NP) if j != i]
    r1, r2, r3 = random.sample(candidates, 3)
    
    mutant = population[r1] + F * (population[r2] - population[r3])
    return mutant

DE/best/1(收敛更快但可能早熟):

def de_best_1(population, fitness, F=0.5, i=None):
    """
    使用当前最优个体作为基准
    """
    NP = len(population)
    best_idx = np.argmin(fitness)
    
    candidates = [j for j in range(NP) if j != i and j != best_idx]
    r1, r2 = random.sample(candidates, 2)
    
    mutant = population[best_idx] + F * (population[r1] - population[r2])
    return mutant

DE/rand/2(更多样化):

def de_rand_2(population, F=0.5, i=None):
    """
    使用两个差分向量
    """
    NP = len(population)
    candidates = [j for j in range(NP) if j != i]
    r1, r2, r3, r4, r5 = random.sample(candidates, 5)
    
    mutant = population[r1] + F * (population[r2] - population[r3]) + \
             F * (population[r4] - population[r5])
    return mutant

2.2.2 变异策略命名规范

DE的变异策略遵循统一的命名规则:DE/x/y/z

  • x:基向量选择方式(rand/best/target to best/current to rand)
  • y:差分向量数量(1, 2, …)
  • z:交叉类型(bin/binomial/exp/exponential)

完整策略列表

策略名称公式特点
DE/rand/1全局搜索强
DE/best/1收敛快
DE/rand-to-best/1平衡型
DE/best/2激进型
DE/rand/2高变异

2.3 交叉操作

2.3.1 二项式交叉(Binomial Crossover)

def binomial_crossover(target, mutant, CR, bounds):
    """
    二项式交叉:每个维度独立决定来源
    """
    D = len(target)
    trial = np.zeros(D)
    
    # 强制包含至少一个来自变异向量的维度
    j_rand = random.randint(0, D-1)
    
    for j in range(D):
        if random.random() < CR or j == j_rand:
            trial[j] = mutant[j]
        else:
            trial[j] = target[j]
    
    # 边界处理
    trial = np.clip(trial, 
                    [b[0] for b in bounds], 
                    [b[1] for b in bounds])
    
    return trial

2.3.2 指数交叉(Exponential Crossover)

def exponential_crossover(target, mutant, CR, bounds):
    """
    指数交叉:连续一段来自变异向量
    """
    D = len(target)
    trial = target.copy()
    
    # 随机选择起始点
    n = random.randint(0, D-1)
    L = 0
    
    # 确定连续段长度
    while random.random() < CR and L < D:
        n = (n + 1) % D
        L += 1
    
    # 复制连续段
    for _ in range(L):
        trial[n] = mutant[n]
        n = (n + 1) % D
    
    # 边界处理
    trial = np.clip(trial, 
                    [b[0] for b in bounds], 
                    [b[1] for b in bounds])
    
    return trial

二项式 vs 指数交叉

特征二项式交叉指数交叉
搜索特性均匀探索局部搜索
参数敏感性CRCR
适用场景高维问题低维问题

2.4 选择操作

def select(target, trial, fitness_target, fitness_trial):
    """
    一对一选择:贪婪策略
    """
    if fitness_trial <= fitness_target:  # 最小化问题
        return trial, fitness_trial
    else:
        return target, fitness_target

数学表达


三、完整算法实现

3.1 标准差分进化

class DifferentialEvolution:
    def __init__(self, objective, bounds, NP=None, F=0.5, CR=0.9, 
                 max_iter=1000, tol=1e-8):
        self.objective = objective
        self.bounds = bounds
        self.D = len(bounds)
        
        # 默认种群规模
        self.NP = NP if NP else max(10 * self.D, 50)
        
        self.F = F      # 缩放因子
        self.CR = CR    # 交叉概率
        self.max_iter = max_iter
        self.tol = tol
        
        self.best_fitness = float('inf')
        self.best_solution = None
        self.history = []
    
    def initialize(self):
        self.population = np.array([
            [np.random.uniform(low, high) 
             for low, high in self.bounds]
            for _ in range(self.NP)
        ])
        self.fitness = np.array([
            self.objective(x) for x in self.population
        ])
        
        best_idx = np.argmin(self.fitness)
        self.best_fitness = self.fitness[best_idx]
        self.best_solution = self.population[best_idx].copy()
    
    def mutate(self, i):
        """DE/rand/1/bin"""
        while True:
            r = random.sample(range(self.NP), 3)
            if i not in r:
                break
        
        r1, r2, r3 = r
        mutant = self.population[r1] + self.F * (
            self.population[r2] - self.population[r3]
        )
        return mutant
    
    def crossover(self, target, mutant):
        """二项式交叉"""
        trial = np.zeros(self.D)
        j_rand = random.randint(0, self.D - 1)
        
        for j in range(self.D):
            if random.random() < self.CR or j == j_rand:
                trial[j] = mutant[j]
            else:
                trial[j] = target[j]
        
        # 边界处理
        for j in range(self.D):
            low, high = self.bounds[j]
            trial[j] = max(low, min(high, trial[j]))
        
        return trial
    
    def step(self):
        """执行一代进化"""
        new_population = np.zeros_like(self.population)
        new_fitness = np.zeros(self.NP)
        
        for i in range(self.NP):
            # 变异
            mutant = self.mutate(i)
            
            # 交叉
            trial = self.crossover(self.population[i], mutant)
            
            # 选择
            fitness_trial = self.objective(trial)
            
            if fitness_trial <= self.fitness[i]:
                new_population[i] = trial
                new_fitness[i] = fitness_trial
            else:
                new_population[i] = self.population[i]
                new_fitness[i] = self.fitness[i]
        
        self.population = new_population
        self.fitness = new_fitness
        
        # 更新最优解
        best_idx = np.argmin(self.fitness)
        if self.fitness[best_idx] < self.best_fitness:
            self.best_fitness = self.fitness[best_idx]
            self.best_solution = self.population[best_idx].copy()
    
    def run(self):
        self.initialize()
        
        for gen in range(self.max_iter):
            self.step()
            self.history.append(self.best_fitness)
            
            if gen % 100 == 0:
                print(f"Gen {gen}: Best = {self.best_fitness:.8f}")
            
            # 早停条件
            if self.best_fitness < self.tol:
                break
        
        return self.best_solution, self.best_fitness

3.2 高级变体:jDE(自适应DE)

class JDE(DifferentialEvolution):
    """
    动态参数自适应差分进化
    Brest et al., 2006
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # 每个个体独立控制参数
        self.F = np.ones(self.NP) * 0.5
        self.CR = np.zeros(self.NP)
        
        # 自适应参数范围
        self.F_l, self.F_u = 0.1, 1.0
        self.CR_l, self.CR_u = 0.0, 1.0
        
        # 自适应概率
        self.tau1 = self.tau2 = 0.1
    
    def update_parameters(self, i):
        """动态更新F和CR"""
        # 更新F
        if random.random() < self.tau1:
            self.F[i] = self.F_l + random.random() * (self.F_u - self.F_l)
        
        # 更新CR
        if random.random() < self.tau2:
            self.CR[i] = self.CR_l + random.random() * (self.CR_u - self.CR_l)
    
    def mutate(self, i):
        """使用个体自适应参数"""
        self.update_parameters(i)
        
        while True:
            r = random.sample(range(self.NP), 3)
            if i not in r:
                break
        
        r1, r2, r3 = r
        mutant = self.population[r1] + self.F[i] * (
            self.population[r2] - self.population[r3]
        )
        return mutant
    
    def crossover(self, target, mutant, CR_i):
        """使用个体自适应交叉率"""
        trial = np.zeros(self.D)
        j_rand = random.randint(0, self.D - 1)
        
        for j in range(self.D):
            if random.random() < CR_i or j == j_rand:
                trial[j] = mutant[j]
            else:
                trial[j] = target[j]
        
        for j in range(self.D):
            low, high = self.bounds[j]
            trial[j] = max(low, min(high, trial[j]))
        
        return trial
    
    def step(self):
        new_population = np.zeros_like(self.population)
        new_fitness = np.zeros(self.NP)
        
        for i in range(self.NP):
            mutant = self.mutate(i)
            trial = self.crossover(self.population[i], mutant, self.CR[i])
            
            fitness_trial = self.objective(trial)
            
            if fitness_trial <= self.fitness[i]:
                new_population[i] = trial
                new_fitness[i] = fitness_trial
            else:
                new_population[i] = self.population[i]
                new_fitness[i] = self.fitness[i]
        
        self.population = new_population
        self.fitness = new_fitness
        
        best_idx = np.argmin(self.fitness)
        if self.fitness[best_idx] < self.best_fitness:
            self.best_fitness = self.fitness[best_idx]
            self.best_solution = self.population[best_idx].copy()

四、参数控制与调节

4.1 缩放因子F的影响

控制差分向量的缩放程度,直接影响算法的探索能力:

F值特性适用场景
F < 0.3局部搜索能力强微调精细解
F ≈ 0.5平衡型通用场景
F > 0.8全局探索能力强多模态问题

Warning

F过大(如F > 1.0)可能导致搜索步长过大,跨越最优区域;F过小(如F < 0.2)可能导致搜索停滞,早熟收敛。

4.2 交叉概率CR的影响

CR控制试验向量来自变异向量的比例:

CR值特性适用场景
CR < 0.3接近随机搜索高维复杂问题
CR ≈ 0.5平衡型通用场景
CR > 0.8贪婪搜索单峰问题

4.3 种群规模NP

经验公式

4.4 参数自适应策略

4.4.1 Success History Adaptive DE (SHADE)

class SHADE(DifferentialEvolution):
    """
    基于成功历史的自适应DE
    Tanabe & Fukunaga, 2013
    """
    def __init__(self, *args, H=6, **kwargs):
        super().__init__(*args, **kwargs)
        self.H = H
        
        # 历史记忆
        self.F_means = [0.5] * H
        self.CR_means = [0.5] * H
        self.archive = []  # 存档非优解
        self.k = 0
    
    def update_history(self, successful_F, successful_CR):
        """更新历史记忆"""
        if len(successful_F) == 0:
            return
        
        # 更新记忆
        F_mean = sum(f**2 for f in successful_F) / sum(successful_F)
        CR_mean = np.mean(successful_CR)
        
        self.F_means[self.k] = F_mean
        self.CR_means[self.k] = CR_mean
        
        # 循环更新索引
        self.k = (self.k + 1) % self.H
    
    def sample_parameters(self, i):
        """从历史记忆中采样参数"""
        r = random.randint(0, self.H - 1)
        F = self.F_means[r]
        CR = self.CR_means[r]
        
        # 添加随机扰动
        F = F + 0.1 * random.gauss(0, 1)
        CR = random.gauss(CR, 0.1)
        
        # 限制范围
        F = np.clip(F, 0, 1)
        CR = np.clip(CR, 0, 1)
        
        return F, CR

五、约束优化中的DE

5.1 罚函数方法

class ConstrainedDE(DifferentialEvolution):
    def __init__(self, objective, constraints, *args, penalty=1000, **kwargs):
        super().__init__(objective, *args, **kwargs)
        self.constraints = constraints
        self.penalty = penalty
    
    def evaluate_constraints(self, x):
        """
        返回约束违反程度
        g(x) <= 0: 不等式约束
        h(x) = 0: 等式约束
        """
        g_violations = [max(0, g(x)) for g in self.constraints['ineq']]
        h_violations = [abs(h(x)) for h in self.constraints['eq']]
        
        return g_violations, h_violations
    
    def penalized_fitness(self, x):
        """计算带惩罚的适应度"""
        obj = self.objective(x)
        g_viol, h_viol = self.evaluate_constraints(x)
        
        penalty = self.penalty * (sum(g_viol) + sum(h_viol))
        return obj + penalty

5.2 ε-约束处理方法

class EpsilonConstraintDE(ConstrainedDE):
    def __init__(self, *args, epsilon_0=0, rho=0.1, **kwargs):
        super().__init__(*args, **kwargs)
        self.epsilon_0 = epsilon_0
        self.rho = rho
        self.epsilon = epsilon_0
    
    def update_epsilon(self, generation):
        """更新ε参数"""
        self.epsilon = self.epsilon_0 * (self.rho ** generation)
    
    def comparison(self, x1, x2):
        """基于ε约束的比较"""
        g1, _ = self.evaluate_constraints(x1)
        g2, _ = self.evaluate_constraints(x2)
        
        total_violation1 = sum(g1)
        total_violation2 = sum(g2)
        
        # 优先选择可行解
        if total_violation1 <= self.epsilon and total_violation2 > self.epsilon:
            return x1
        if total_violation2 <= self.epsilon and total_violation1 > self.epsilon:
            return x2
        
        # 都可行或都不可行时比较目标函数值
        if self.objective(x1) <= self.objective(x2):
            return x1
        return x2

5.3 随机排名方法

class StochasticRankingDE(DifferentialEvolution):
    """
    随机排名法处理约束
    Runarsson & Yao, 2000
    """
    def __init__(self, *args, P_f=0.45, **kwargs):
        super().__init__(*args, **kwargs)
        self.P_f = P_f  # 目标比较概率
    
    def compare(self, x1, x2):
        g1, _ = self.evaluate_constraints(x1)
        g2, _ = self.evaluate_constraints(x2)
        
        v1, v2 = sum(g1), sum(g2)
        
        # 都可行
        if v1 == 0 and v2 == 0:
            return x1 if self.objective(x1) <= self.objective(x2) else x2
        
        # 基于约束违反度的比较
        if random.random() < self.P_f:
            return x1 if v1 < v2 else x2
        else:
            return x1 if self.objective(x1) <= self.objective(x2) else x2

六、DE的改进变体

6.1 多目标差分进化

class MODE(DifferentialEvolution):
    """
    多目标差分进化
    """
    def __init__(self, objectives, *args, **kwargs):
        self.objectives = objectives
        self.n_objectives = len(objectives)
        super().__init__(lambda x: 0, *args, **kwargs)  # 占位
    
    def evaluate(self, x):
        return np.array([f(x) for f in self.objectives])
    
    def dominates(self, x1, x2):
        f1 = self.evaluate(x1)
        f2 = self.evaluate(x2)
        return all(f1 <= f2) and any(f1 < f2)
    
    def fast_non_dominated_sort(self, population):
        """快速非支配排序"""
        domination_count = [0] * len(population)
        dominated_set = [set() for _ in range(len(population))]
        fronts = [[]]
        
        for i, p in enumerate(population):
            for j, q in enumerate(population):
                if i != j and self.dominates(p, q):
                    dominated_set[i].add(j)
                elif i != j and self.dominates(q, p):
                    domination_count[i] += 1
            
            if domination_count[i] == 0:
                fronts[0].append(i)
        
        current_front = 0
        while fronts[current_front]:
            next_front = []
            for i in fronts[current_front]:
                for j in dominated_set[i]:
                    domination_count[j] -= 1
                    if domination_count[j] == 0:
                        next_front.append(j)
            current_front += 1
            if next_front:
                fronts.append(next_front)
        
        return fronts[:-1] if fronts[-1] == [] else fronts

6.2 协方差引导的DE

class CMAguidedDE(DifferentialEvolution):
    """
    协方差矩阵引导的差分进化
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.cov_matrix = np.eye(self.D)
    
    def mutate_cma_guided(self, i):
        """使用协方差矩阵引导变异"""
        while True:
            r = random.sample(range(self.NP), 3)
            if i not in r:
                break
        
        r1, r2, r3 = r
        
        # 计算加权差分方向
        L = np.linalg.cholesky(self.cov_matrix)
        scaled_diff = L @ (self.population[r2] - self.population[r3])
        
        mutant = self.population[r1] + self.F * scaled_diff
        return mutant
    
    def update_covariance(self):
        """基于成功个体更新协方差"""
        # 获取最优个体
        best_idx = np.argmin(self.fitness)
        
        # 更新协方差
        pass  # 简化实现

七、算法性能对比

7.1 CEC基准测试表现

在CEC(IEEE Congress on Evolutionary Computation)基准测试中,DE通常在以下函数类型上表现优异:

函数类型DE表现备注
Unimodal★★★★☆收敛速度快
Multimodal★★★☆☆依赖策略选择
Hybrid Composition★★★★☆F参数关键
Multi-Swarm★★★☆☆适合多峰搜索

7.2 参数推荐配置

问题类型FCRNP策略
通用0.50.910×DDE/rand/1/bin
多模态0.6-0.90.3-0.715×DDE/rand/2/bin
高维0.50.5-0.75×DDE/rand/1/exp
约束0.50.820×DDE/best/1/bin

八、实用工具与框架

8.1 使用scipy.optimize

from scipy.optimize import differential_evolution
 
def scipy_de_example():
    bounds = [(-5, 5)] * 10
    
    result = differential_evolution(
        func=objective,
        bounds=bounds,
        strategy='best1bin',
        maxiter=1000,
        popsize=15,
        tol=1e-7,
        mutation=(0.5, 1),
        recombination=0.7,
        seed=None,
        polish=True,  # 使用L-BFGS-B精调
        updating='immediate'
    )
    
    print(f"Optimal value: {result.fun}")
    print(f"Solution: {result.x}")
    return result

8.2 使用jmetalpy

from jmetal.algorithm.singleobjective import DifferentialEvolution
from jmetal.operator import PolynomialMutation
from jmetal.problem import Problem
 
class MyProblem(Problem):
    def __init__(self):
        super().__init__()
        self.number_of_variables = 10
        self.number_of_objectives = 1
        self.number_of_constraints = 0
        self.lower_bound = [-10] * 10
        self.upper_bound = [10] * 10
    
    def evaluate(self, solution):
        # 评估函数
        pass
    
    def get_name(self):
        return "MyProblem"
 
algorithm = DifferentialEvolution(
    problem=MyProblem(),
    population_size=100,
    CR=0.5,
    F=0.5,
    max_evaluations=50000
)

参考文献

  1. Storn, R., & Price, K. (1997). Differential Evolution - A Simple and Efficient Heuristic for Global Optimization over Continuous Spaces. Journal of Global Optimization, 11(4), 341-359.
  2. Brest, J., et al. (2006). Self-Adapting Control Parameters in Differential Evolution: A Comparative Study on Numerical Benchmark Problems. IEEE Transactions on Evolutionary Computation, 10(6), 646-657.
  3. Das, S., & Suganthan, P. N. (2011). Differential Evolution: A Survey of the State-of-the-Art. IEEE Transactions on Evolutionary Computation, 15(1), 4-31.
  4. Tanabe, R., & Fukunaga, A. (2013). Success-History Based Parameter Adaptation for Differential Evolution. Proceedings of IEEE CEC, 2013.
  5. Price, K. V., et al. (2005). Differential Evolution: A Practical Approach to Global Optimization. Springer.

相关文档