news 2026/2/24 18:46:31

进化算法求解约束多目标优化问题【附代码】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进化算法求解约束多目标优化问题【附代码】

博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。

✅成品或者定制,扫描文章底部微信二维码。


(1) 基于分解的自适应约束处理二三目标差分进化算法

约束多目标优化问题在工程设计和科学研究中普遍存在,其特点是同时存在多个相互冲突的优化目标和若干约束条件。与无约束多目标优化相比,约束的存在使得问题求解更加困难,因为算法不仅需要在目标空间中寻找分布良好的Pareto最优解,还必须保证这些解满足所有约束条件。当约束条件较为苛刻时,可行域可能仅占搜索空间的很小比例,算法难以找到足够数量的可行解,更难以逼近真实的约束Pareto前沿。

基于分解的多目标优化方法将多目标问题转化为一组单目标子问题,通过协同求解这些子问题来逼近整个Pareto前沿。分解的核心是定义一组均匀分布的权重向量,每个权重向量对应一个子问题,子问题的标量化目标函数由各目标函数按权重加权聚合得到。常用的聚合方式包括加权和方法、切比雪夫方法和边界交叉方法等,不同的聚合方式具有不同的等值线形状,适用于不同类型的Pareto前沿。通过优化各子问题并维护子问题之间的邻域协作关系,分解方法能够在保持解多样性的同时提高收敛效率。

自适应epsilon约束处理技术是处理约束的核心机制。epsilon水平值定义了当前可接受的约束违反程度,约束违反量小于epsilon的个体被视为可行个体参与正常的优化过程。epsilon值的设置和调整对算法性能有重要影响,值设得太小会使得初始阶段难以找到可行解,值设得太大则会导致大量不可行解进入种群降低收敛速度。自适应机制根据当前种群的可行解比例动态调整epsilon值,当可行解比例较低时增大epsilon放宽约束以增加可行个体数量,当可行解比例较高时减小epsilon收紧约束以提高解的质量。

改进的选择操作充分利用了具有较小目标函数值的不可行个体所包含的信息。传统方法通常直接丢弃不可行解或对其施加严重惩罚,但这样做忽视了不可行解可能指向可行域边界上的优质解这一事实。改进的选择策略允许约束违反量较小且目标函数值较优的不可行个体以一定概率被选入下一代种群,这些个体可以作为跳板引导种群向可行域的优质区域移动。选择概率与约束违反量呈负相关,违反量越小被选中的概率越大,从而在利用不可行解信息和维护种群可行性之间取得平衡。

基于差分进化思想的交叉策略进一步提升了搜索效率。差分进化是一种简单高效的进化算法,其核心操作是利用种群个体之间的差向量进行变异。在约束多目标优化中,将差分进化的变异策略与分解方法相结合,对每个子问题,从其邻域内选择个体构造差向量,产生的试验个体能够有效利用邻域信息进行局部搜索。此外,还设计了自适应控制参数调整机制,变异因子和交叉概率根据子问题的优化状态动态调整,收敛较慢的子问题采用较大的参数值增强探索,收敛较快的子问题采用较小的参数值加强开发。

(2) 基于角度信息的高效约束高维多目标进化算法

随着优化目标数目的增加,多目标优化问题的求解难度急剧上升。当目标数超过三个时,传统的Pareto支配关系变得越来越难以区分个体优劣,因为大多数个体都是非支配的。这种现象被称为支配关系的劣化,导致基于Pareto支配的选择操作失去选择压力,算法难以向真实Pareto前沿收敛。高维多目标优化需要采用新的选择机制来弥补Pareto支配的不足,常见的策略包括指标选择、参考点选择和分解选择等。

约束高维多目标优化问题进一步增加了问题的复杂性。在高维目标空间中,可行域的形状可能非常复杂,部分区域的可行解稀疏甚至不存在。现有的约束高维多目标进化算法为了保证收敛精度,通常采用较为保守的约束处理策略,优先发展可行解而抑制不可行解,这种策略虽然能够保证最终解的可行性,但收敛速度较慢,在有限的计算资源下难以逼近真实Pareto前沿。

基于角度违反度函数的选择操作是加速收敛的核心创新。角度信息描述了个体在目标空间中相对于理想点的方向,方向相近的个体具有相似的目标函数比例关系,可以认为它们在搜索同一部分Pareto前沿。角度违反度则衡量了个体方向与其最近参考向量之间的偏离程度,偏离越大说明个体在该方向上的代表性越差。选择操作首先按非支配等级分层,在同一非支配层内,根据角度违反度选择与各参考向量方向最接近的个体,同时考虑约束违反度对选择概率的调节作用。

动态平衡收敛性和分布性是选择策略的关键设计。在优化初期,种群离Pareto前沿较远,此时应优先考虑收敛性,选择目标函数值较优的个体即使其分布不够均匀;在优化后期,种群已接近Pareto前沿,此时应强调分布性,确保解在整个前沿均匀分布。通过引入与迭代进度相关的动态权重,在选择操作中自适应调节收敛性和分布性的相对重要程度,实现不同优化阶段的灵活切换。

差分进化交叉策略与比例因数设置相互配合提升算法性能。比例因数控制了可行解与不可行解在交叉操作中的参与比例,随着迭代进行动态调整。初期比例因数较大,允许更多不可行解参与交叉以扩大搜索范围;后期比例因数减小,限制不可行解的参与以保证子代的可行性。交叉操作本身采用改进的差分进化策略,引入外部档案中的优秀个体作为差向量的基础,将历史优质信息融入当前搜索过程。

实验验证在标准的约束高维多目标优化测试问题集上进行,涵盖了三到十个目标的各种问题实例。与多种先进的约束高维多目标进化算法进行对比,评价指标包括反世代距离、超体积和均匀性指标等。结果表明,所提算法在收敛速度方面具有明显优势,在相同的函数评价次数下能够获得更接近真实Pareto前沿的解集,同时在分布性方面也不逊于对比算法,证明了角度选择策略和动态平衡机制的有效性。

(3) 基于协同进化的等式约束多目标优化算法及工程应用

等式约束是约束优化问题中最难处理的约束类型。与不等式约束定义一个体积域不同,等式约束定义的可行域是一个低维流形,从测度论的角度看可行域的体积为零。这意味着在搜索空间中随机采样几乎不可能得到严格可行的点,必须借助特殊技术将不可行点引导到可行流形上。常见的处理方法包括松弛化方法、修复方法和惩罚方法,但这些方法在处理多个复杂等式约束时往往效果不佳。

协同进化框架为等式约束多目标优化提供了新的思路。算法维护两个相互协作的种群:主种群和辅助种群。主种群负责在可行域附近进行搜索,其优化目标是在满足约束的前提下逼近Pareto前沿。辅助种群则不考虑约束条件,完全在目标空间中进行自由搜索,其作用是探索可能被主种群忽略的搜索空间区域,发现有潜力的进化方向。两个种群之间通过个体迁移进行信息交流,辅助种群中的优秀个体可以迁入主种群提供新的遗传材料,主种群中接近可行的个体可以迁入辅助种群指导探索方向。

针对两个种群设计了不同的交叉策略以适应各自的搜索任务。主种群的交叉策略强调开发性能,采用较小的步长和较高的交叉概率,在可行域附近进行精细搜索。当父代个体都是可行的时,产生的子代也更可能落在可行域内或附近,有利于维持种群的可行性。辅助种群的交叉策略强调探索性能,采用较大的步长和引入随机扰动,在更广阔的范围内搜索可能的优质区域。两种策略相互补充,既保证了可行解的持续改进,又避免了过早收敛到局部区域。

修复操作是提高等式约束满足程度的有效手段。对于交叉和变异产生的子代个体,如果其等式约束违反量超过阈值,则触发修复操作。修复的基本思想是沿着约束函数的梯度方向调整个体位置,使其向等式约束定义的可行流形靠近。由于等式约束通常是非线性的,单次梯度调整可能不足以达到严格可行,因此采用迭代修复策略,反复进行梯度投影直到约束违反量足够小或达到最大迭代次数。修复操作使得更多个体能够落在可行域附近,增加了可行解的供应量。

import numpy as np from pymoo.core.problem import Problem from pymoo.algorithms.moo.nsga2 import NSGA2 from pymoo.operators.crossover.sbx import SBX from pymoo.operators.mutation.pm import PM from pymoo.optimize import minimize from scipy.spatial.distance import cdist class ConstrainedMOEA: def __init__(self, n_var, n_obj, n_constr, xl, xu): self.n_var = n_var self.n_obj = n_obj self.n_constr = n_constr self.xl = np.array(xl) self.xu = np.array(xu) self.epsilon = 0.1 self.archive = [] def evaluate(self, x): pass def calculate_cv(self, g): return np.sum(np.maximum(0, g), axis=1) def adaptive_epsilon_update(self, cv_values, gen, max_gen): feasible_ratio = np.sum(cv_values == 0) / len(cv_values) if feasible_ratio < 0.2: self.epsilon = self.epsilon * 1.1 elif feasible_ratio > 0.8: self.epsilon = self.epsilon * 0.9 self.epsilon = max(0.0001, min(1.0, self.epsilon)) cp = 1 - gen / max_gen self.epsilon = self.epsilon * cp def generate_weight_vectors(self, n_obj, n_partitions): if n_obj == 2: weights = np.zeros((n_partitions + 1, 2)) for i in range(n_partitions + 1): weights[i] = [i / n_partitions, 1 - i / n_partitions] return weights else: from itertools import combinations_with_replacement weights = [] for c in combinations_with_replacement(range(n_partitions + 1), n_obj - 1): c = (0,) + c + (n_partitions,) w = np.diff(c) / n_partitions weights.append(w) return np.array(weights) def decomposition_selection(self, population, fitness, cv, weights): n_pop = len(population) selected_indices = [] ideal_point = np.min(fitness, axis=0) for i, w in enumerate(weights): tchebycheff = np.max(np.abs(fitness - ideal_point) * w, axis=1) feasible_mask = cv <= self.epsilon if np.any(feasible_mask): feasible_tcheb = tchebycheff.copy() feasible_tcheb[~feasible_mask] = np.inf best_idx = np.argmin(feasible_tcheb) else: combined = tchebycheff + 1000 * cv best_idx = np.argmin(combined) selected_indices.append(best_idx) return list(set(selected_indices)) def differential_evolution_crossover(self, population, idx, F=0.5, CR=0.9): n_pop, n_var = population.shape indices = list(range(n_pop)) indices.remove(idx) r1, r2, r3 = np.random.choice(indices, 3, replace=False) mutant = population[r1] + F * (population[r2] - population[r3]) trial = population[idx].copy() j_rand = np.random.randint(n_var) for j in range(n_var): if np.random.rand() < CR or j == j_rand: trial[j] = mutant[j] trial = np.clip(trial, self.xl, self.xu) return trial def angle_based_selection(self, population, fitness, cv, n_select, ref_vectors): ideal = np.min(fitness, axis=0) nadir = np.max(fitness, axis=0) normalized = (fitness - ideal) / (nadir - ideal + 1e-10) angles = np.arccos(np.clip(normalized @ ref_vectors.T / (np.linalg.norm(normalized, axis=1, keepdims=True) * np.linalg.norm(ref_vectors, axis=1) + 1e-10), -1, 1)) associations = np.argmin(angles, axis=1) selected = [] for i in range(len(ref_vectors)): mask = associations == i if np.any(mask): candidates = np.where(mask)[0] feasible_cand = candidates[cv[candidates] <= self.epsilon] if len(feasible_cand) > 0: best = feasible_cand[np.argmin(angles[feasible_cand, i])] else: combined_score = angles[candidates, i] + 10 * cv[candidates] best = candidates[np.argmin(combined_score)] selected.append(best) while len(selected) < n_select: remaining = list(set(range(len(population))) - set(selected)) if remaining: selected.append(np.random.choice(remaining)) else: break return selected[:n_select] class CoEvolutionaryConstrainedMOEA(ConstrainedMOEA): def __init__(self, n_var, n_obj, n_constr, xl, xu, obj_func, constr_func): super().__init__(n_var, n_obj, n_constr, xl, xu) self.obj_func = obj_func self.constr_func = constr_func def optimize(self, pop_size=100, max_gen=200): main_pop = np.random.uniform(self.xl, self.xu, (pop_size, self.n_var)) aux_pop = np.random.uniform(self.xl, self.xu, (pop_size, self.n_var)) ref_vectors = self.generate_weight_vectors(self.n_obj, 20) convergence = [] for gen in range(max_gen): main_fitness = np.array([self.obj_func(x) for x in main_pop]) main_cv = np.array([np.sum(np.maximum(0, self.constr_func(x))) for x in main_pop]) aux_fitness = np.array([self.obj_func(x) for x in aux_pop]) self.adaptive_epsilon_update(main_cv, gen, max_gen) offspring_main = [] for i in range(pop_size): trial = self.differential_evolution_crossover(main_pop, i, F=0.5, CR=0.9) trial = self.repair_solution(trial) offspring_main.append(trial) offspring_main = np.array(offspring_main) offspring_aux = [] for i in range(pop_size): trial = self.differential_evolution_crossover(aux_pop, i, F=0.8, CR=0.7) offspring_aux.append(trial) offspring_aux = np.array(offspring_aux) combined_main = np.vstack([main_pop, offspring_main]) combined_fitness = np.array([self.obj_func(x) for x in combined_main]) combined_cv = np.array([np.sum(np.maximum(0, self.constr_func(x))) for x in combined_main]) selected_idx = self.angle_based_selection(combined_main, combined_fitness, combined_cv, pop_size, ref_vectors) main_pop = combined_main[selected_idx] combined_aux = np.vstack([aux_pop, offspring_aux]) aux_fitness_all = np.array([self.obj_func(x) for x in combined_aux]) ranks = self.non_dominated_sort(aux_fitness_all) sorted_idx = np.argsort(ranks)[:pop_size] aux_pop = combined_aux[sorted_idx] if gen % 10 == 0: n_migrate = pop_size // 10 aux_best_idx = np.argsort(ranks)[:n_migrate] main_worst_idx = np.argsort(main_cv)[-n_migrate:] main_pop[main_worst_idx] = aux_pop[aux_best_idx] feasible_mask = combined_cv[selected_idx] == 0 if np.any(feasible_mask): best_idx = np.argmin(combined_fitness[selected_idx][feasible_mask, 0]) convergence.append(combined_fitness[selected_idx][feasible_mask][best_idx, 0]) else: convergence.append(np.inf) return main_pop, np.array([self.obj_func(x) for x in main_pop]), convergence def repair_solution(self, x, max_iter=10): for _ in range(max_iter): g = self.constr_func(x) cv = np.sum(np.maximum(0, g)) if cv < 1e-6: break grad = np.zeros_like(x) eps = 1e-6 for j in range(len(x)): x_plus = x.copy() x_plus[j] += eps grad[j] = (np.sum(np.maximum(0, self.constr_func(x_plus))) - cv) / eps step = -0.1 * grad x = x + step x = np.clip(x, self.xl, self.xu) return x def non_dominated_sort(self, fitness): n = len(fitness) ranks = np.zeros(n, dtype=int) domination_count = np.zeros(n, dtype=int) dominated_set = [[] for _ in range(n)] for i in range(n): for j in range(i + 1, n): if np.all(fitness[i] <= fitness[j]) and np.any(fitness[i] < fitness[j]): dominated_set[i].append(j) domination_count[j] += 1 elif np.all(fitness[j] <= fitness[i]) and np.any(fitness[j] < fitness[i]): dominated_set[j].append(i) domination_count[i] += 1 current_front = np.where(domination_count == 0)[0].tolist() rank = 0 while current_front: for i in current_front: ranks[i] = rank for j in dominated_set[i]: domination_count[j] -= 1 current_front = np.where(domination_count == 0)[0].tolist() current_front = [i for i in current_front if ranks[i] == 0 and i not in current_front] rank += 1 return ranks def example_objective(x): f1 = x[0] f2 = (1 + x[1]) / (x[0] + 1e-10) return np.array([f1, f2]) def example_constraints(x): g1 = x[0] + x[1] - 1 g2 = -x[0] - x[1] + 0.5 return np.array([g1, g2]) if __name__ == "__main__": optimizer = CoEvolutionaryConstrainedMOEA( n_var=2, n_obj=2, n_constr=2, xl=[0, 0], xu=[1, 1], obj_func=example_objective, constr_func=example_constraints ) final_pop, final_fitness, conv = optimizer.optimize(pop_size=100, max_gen=200) print(f"Found {len(final_pop)} solutions") print(f"Best objective values: {final_fitness[0]}")


成品代码50-200,定制300起,可以直接沟通

👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/20 5:47:38

揭秘Docker容器安全漏洞:Cilium Network Policy如何构建坚不可摧的防护墙?

第一章&#xff1a;Docker容器安全威胁全景洞察Docker 作为主流的容器化技术&#xff0c;极大提升了应用部署效率与资源利用率。然而&#xff0c;其共享内核、动态编排和镜像分发机制也引入了新的攻击面。理解这些潜在威胁是构建安全容器环境的前提。镜像来源不可信 使用未经验…

作者头像 李华
网站建设 2026/2/15 3:52:03

AIME24得分80.3!这款15亿参数模型正在改写数学推理格局

VibeThinker-1.5B&#xff1a;小模型如何在数学推理中实现“降维打击”&#xff1f; 在AIME24&#xff08;美国数学邀请赛2024&#xff09;的模拟评测中&#xff0c;一款仅含15亿参数的模型拿下了80.3分——这个数字不仅超过了初始版DeepSeek R1&#xff08;79.8&#xff09;&…

作者头像 李华
网站建设 2026/2/16 4:22:47

Baidu PaddlePaddle模型部署:VibeThinker生成Serving服务脚本

Baidu PaddlePaddle模型部署&#xff1a;VibeThinker生成Serving服务脚本 在AI工程落地的实践中&#xff0c;一个训练好的模型若无法高效服务于真实业务场景&#xff0c;其价值将大打折扣。尤其是在数学推理、算法编程等专业领域&#xff0c;开发者往往面临这样的困境&#xff…

作者头像 李华
网站建设 2026/2/17 16:18:39

Appium移动测试框架全解析

移动测试的变革与Appium的崛起在移动应用爆发的时代&#xff0c;自动化测试已成为软件测试从业者的必备技能。Appium作为一款开源的移动测试框架&#xff0c;凭借其跨平台支持和灵活性&#xff0c;迅速成为行业标准。据2025年行业报告&#xff0c;超过70%的企业在移动测试中采用…

作者头像 李华
网站建设 2026/2/23 20:13:56

【文献速递】体外激酶实验揭示LATS1/2磷酸化STAT1的新调控轴

01 研究背景子宫内膜癌中LATS1/2的高频突变与MHC-I表达下调的关联&#xff0c;提示了这两个Hippo通路核心激酶可能参与了免疫调控。然而&#xff0c;传统认知中LATS1/2主要通过磷酸化YAP/TAZ发挥作用&#xff0c;其是否以及如何调控MHC-I表达完全未知。STAT1作为干扰素信号通路…

作者头像 李华