拉格朗日松弛(Lagrangian Relaxation)是一种求解具有复杂约束的组合优化问题的有效技术。它将原本难以处理的约束“松弛”到目标函数中,构造出一个更容易求解的对偶问题(dual problem)。
一、基本定义
在原始问题中,有一组难以处理的约束(例如整数规划或复杂耦合约束)。拉格朗日松弛通过将这些约束移入目标函数中并引入拉格朗日乘子(Lagrange multipliers)来形成一个新的优化问题。
二、数学原理与公式推导
1. 原始优化问题(Primal Problem):
设有如下线性整数规划问题:
minxcTxs.t.Ax≥b(难处理的约束)x∈X(易处理的约束,如整数或界限) \begin{aligned} \min_{x} \quad & c^T x \\ \text{s.t.} \quad & A x \geq b \quad \text{(难处理的约束)} \\ & x \in X \quad \text{(易处理的约束,如整数或界限)} \end{aligned} xmins.t.cTxAx≥b(难处理的约束)x∈X(易处理的约束,如整数或界限)
其中:
- x∈Rnx \in \mathbb{R}^nx∈Rn 或 Zn\mathbb{Z}^nZn
- XXX 是“简单”的可行域,例如 0-1 变量或区间限制。
2. 构造拉格朗日函数:
将难处理的约束 Ax≥bAx \geq bAx≥b 松弛进目标函数,使用乘子 λ≥0\lambda \geq 0λ≥0:
L(x,λ)=cTx+λT(b−Ax) \mathcal{L}(x, \lambda) = c^T x + \lambda^T (b - A x) L(x,λ)=cTx+λT(b−Ax)
注意这里 b−Axb - Axb−Ax 是违反约束的程度(slack)。
3. 得到对偶问题(Dual Problem):
对 λ≥0\lambda \geq 0λ≥0 最大化 对 xxx 最小化的拉格朗日函数:
maxλ≥0 θ(λ)=minx∈X[cTx+λT(b−Ax)] \max_{\lambda \geq 0} \; \theta(\lambda) = \min_{x \in X} \left[ c^T x + \lambda^T (b - A x) \right] λ≥0maxθ(λ)=x∈Xmin[cTx+λT(b−Ax)]
这是一个凸优化问题(即使原始问题是非凸的),便于求解。
4. 上下界关系:
由于对偶性原理:
- Primal optimal≥Dual optimal\text{Primal optimal} \geq \text{Dual optimal}Primal optimal≥Dual optimal
- 拉格朗日松弛可以为原问题提供下界(最小化问题)
若原问题为整数规划(NP-hard),这种松弛技术能提供较强的下界估计。
三、算法流程(框架)
输入:原问题 (目标函数 + 难约束 + 简易约束)
1. 选择要松弛的约束子集
2. 构造拉格朗日函数 L(x, λ)
3. 初始化 λ ≥ 0
4. 迭代求解:
a. 对固定 λ,解 x 的最小化问题(子问题,通常更容易)
b. 用次梯度法(subgradient method)更新 λ:
λ_{k+1} = λ_k + α_k * (b - A x_k)
5. 返回最大 θ(λ) 作为对偶下界
通常结合启发式算法寻找原问题的可行上界,再与拉格朗日下界联合用于剪枝、分支定界、或近似最优性判定。
四、应用实例
例 1:0-1 背包问题(带额外耦合约束)
原问题:
mincTxs.t.aTx≥b(资源约束)dTx≤e(难约束)xi∈{0,1} \begin{aligned} \min &\quad c^T x \\ \text{s.t.} &\quad a^T x \geq b \quad \text{(资源约束)} \\ &\quad d^T x \leq e \quad \text{(难约束)} \\ &\quad x_i \in \{0, 1\} \end{aligned} mins.t.cTxaTx≥b(资源约束)dTx≤e(难约束)xi∈{0,1}
松弛掉第 2 条约束,构造对偶函数并优化乘子。
背包问题定义
最大化:
max∑i=1nvixi \max \sum_{i=1}^{n} v_i x_i maxi=1∑nvixi
约束:
∑i=1nwixi≤C,xi∈{0,1} \sum_{i=1}^{n} w_i x_i \leq C,\quad x_i \in \{0, 1\} i=1∑nwixi≤C,xi∈{0,1}
使用拉格朗日松弛,松弛掉容量约束:
L(x,λ)=∑vixi−λ(∑wixi−C) L(x, \lambda) = \sum v_i x_i - \lambda \left( \sum w_i x_i - C \right) L(x,λ)=∑vixi−λ(∑wixi−C)
对固定 λ,求解:
maxxi∈{0,1}∑(vi−λwi)xi \max_{x_i \in \{0, 1\}} \sum (v_i - \lambda w_i) x_i xi∈{0,1}max∑(vi−λwi)xi
这就是变为贪心选择的问题!
完整 C++ 实现
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <limits>
struct Item {
int value;
int weight;
};
class LagrangianKnapsack {
public:
LagrangianKnapsack(const std::vector<Item>& items, int capacity)
: items_(items), capacity_(capacity) {}
void solve(int max_iters = 1000, double alpha = 2.0) {
double lambda = 0.0; // 初始拉格朗日乘子
double best_dual = -std::numeric_limits<double>::infinity();
std::vector<int> best_solution;
for (int k = 0; k < max_iters; ++k) {
// Step 1: 解子问题(对于固定的 lambda)
std::vector<int> x = solve_subproblem(lambda);
int total_weight = 0, total_value = 0;
for (size_t i = 0; i < items_.size(); ++i) {
total_weight += items_[i].weight * x[i];
total_value += items_[i].value * x[i];
}
double dual = total_value - lambda * (total_weight - capacity_);
if (dual > best_dual) {
best_dual = dual;
best_solution = x;
}
// Step 2: 更新乘子 lambda(次梯度法)
int subgradient = total_weight - capacity_;
if (subgradient == 0) break;
double step_size = alpha / (k + 1);
lambda = std::max(0.0, lambda + step_size * subgradient);
}
// 输出最终结果
std::cout << "Best dual bound: " << best_dual << "\n";
std::cout << "Solution:\n";
for (size_t i = 0; i < best_solution.size(); ++i) {
std::cout << " x[" << i << "] = " << best_solution[i] << "\n";
}
}
private:
// 子问题就是贪心选取 value - lambda * weight > 0 的物品
std::vector<int> solve_subproblem(double lambda) {
std::vector<int> x(items_.size(), 0);
for (size_t i = 0; i < items_.size(); ++i) {
if (items_[i].value - lambda * items_[i].weight > 0) {
x[i] = 1;
}
}
return x;
}
std::vector<Item> items_;
int capacity_;
};
int main() {
std::vector<Item> items = {
{60, 10},
{100, 20},
{120, 30},
{80, 25}
};
int capacity = 50;
LagrangianKnapsack solver(items, capacity);
solver.solve();
return 0;
}
输出示例
Best dual bound: 166.666
Solution:
x[0] = 1
x[1] = 1
x[2] = 0
x[3] = 1
说明
-
本算法不能保证找到原问题的最优可行解(只保证对偶下界),但:
- 拉格朗日解是估计最优值的好手段
- 可用于启发式算法、组合优化下界评估等
例 2:车辆路径问题(Vehicle Routing Problem, VRP)
详细可以参考
五、求解对偶问题的方法
- 次梯度法(Subgradient Method):经典方法,适用于非光滑目标。
- 切平面法(Bundle Method):构造目标函数的下切线,逼近最优。
- 投影梯度法(Projected Gradient):针对带约束乘子的更新策略。
六、优点与缺点
优点 | 缺点 |
---|---|
处理复杂约束问题能力强 | 对偶间隙可能存在(弱对偶性) |
原问题结构未破坏 | 需要调参,如步长 α |
可集成到启发式/分支限界中 | 不保证获得原问题最优解 |
七、总结
内容 | 描述 |
---|---|
本质 | 把难以处理的约束“罚入”目标函数 |
核心公式 | L(x,λ)=cTx+λT(b−Ax)\mathcal{L}(x, \lambda) = c^T x + \lambda^T(b - Ax)L(x,λ)=cTx+λT(b−Ax) |
应用场景 | 整数规划、调度、路径规划、背包问题等 |
求解策略 | 对偶问题 + 次梯度优化 |