如何编写迭代添加含历次最优解约束的优化问题代码?
解决CVX迭代保存历史最优解与添加切平面约束的问题
你遇到的核心问题是CVX的求解块(cvx_begin/cvx_end)是单次求解逻辑,块内的变量赋值只会在整个求解完成后执行一次,所以原来的代码没法在迭代中累积历史解。要实现基于切平面的逐点极大函数近似,得把迭代逻辑放在CVX块外面,每次循环重新构建带历史约束的优化问题,具体步骤和代码如下:
核心思路
对于凸约束函数 ( f(x) \leq 0 )(( f ) 为凸函数),切平面近似的本质是在每次迭代的最优解 ( x_k ) 处,添加线性约束 ( f(x_k) + \nabla f(x_k)^T(x - x_k) \leq 0 ),用这些线性约束的逐点极大来近似原凸约束。我们需要:
- 在循环外初始化历史解存储、迭代参数
- 每次循环调用CVX求解带新增切平面约束的问题
- 保存每次的最优解,直到满足收敛条件
修正后的代码示例
假设你的目标函数是 ( \frac{1}{2}x^T Q x + c^T x ),原始凸约束是 ( f(x) = |x|_2^2 - 1 \leq 0 )(仅作示例,可替换为你的实际约束):
% 初始化参数 n = 2; % 变量维度 Q = eye(n); c = [-1; -1]; max_iter = 20; % 最大迭代次数 tol = 1e-6; % 收敛阈值 X = []; % 存储历史最优解 prev_obj = inf; % 上一次迭代的目标函数值 % 第一次求解:仅原始约束 cvx_begin variable x(n,1) minimize(0.5 * x' * Q * x + c' * x) subject to norm(x, 2)^2 <= 1; % 原始凸约束 cvx_end X = [X, x]; prev_obj = 0.5 * x' * Q * x + c' * x; % 迭代添加切平面约束 for iter = 1:max_iter % 获取上一次的最优解 x_prev = X(:, end); % 计算f(x_prev)和梯度∇f(x_prev)(根据你的实际约束替换) f_prev = norm(x_prev, 2)^2 - 1; grad_f_prev = 2 * x_prev; % 构建带新增切平面约束的CVX问题 cvx_begin variable x(n,1) minimize(0.5 * x' * Q * x + c' * x) subject to norm(x, 2)^2 <= 1; % 原始约束保留 % 添加历史所有切平面约束(也可只加最新的,收敛效果类似) for k = 1:size(X,2) x_k = X(:, k); f_k = norm(x_k, 2)^2 - 1; grad_f_k = 2 * x_k; f_k + grad_f_k' * (x - x_k) <= 0; end cvx_end % 保存当前最优解 X = [X, x]; % 计算当前目标函数值 curr_obj = 0.5 * x' * Q * x + c' * x; % 检查收敛 if abs(curr_obj - prev_obj) < tol fprintf('迭代收敛,共迭代%d次\n', iter); break; end prev_obj = curr_obj; end % 查看历史最优解 disp('历次迭代最优解:'); disp(X);
关键说明
- 迭代逻辑放在CVX块外:每次循环都重新定义CVX变量和约束,确保每次求解的是带新增切平面的问题
- 历史约束的添加:可以选择每次只加最新的切平面约束,或者累积所有历史约束(两种方式都能收敛,后者可能收敛更快但约束数量会增加)
- 收敛判断:通过比较相邻两次迭代的目标函数值,或者解的欧氏距离来判断是否停止迭代
- 梯度计算:需要你根据实际的凸约束函数手动计算梯度;如果函数不可导,可以用次梯度替代
内容的提问来源于stack exchange,提问作者Takumi
相关产品推荐
相关产品推荐

