拉格朗日松弛算法(Lagrangian Relaxation)详解和解决背包问题实例(C++实现)

拉格朗日松弛(Lagrangian Relaxation)是一种求解具有复杂约束的组合优化问题的有效技术。它将原本难以处理的约束“松弛”到目标函数中,构造出一个更容易求解的对偶问题(dual problem)


一、基本定义

原始问题中,有一组难以处理的约束(例如整数规划或复杂耦合约束)。拉格朗日松弛通过将这些约束移入目标函数中并引入拉格朗日乘子(Lagrange multipliers)来形成一个新的优化问题。


二、数学原理与公式推导

1. 原始优化问题(Primal Problem):

设有如下线性整数规划问题:

min⁡xcTxs.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.cTxAxb(难处理的约束)xX(易处理的约束,如整数或界限)

其中:

  • x∈Rnx \in \mathbb{R}^nxRnZn\mathbb{Z}^nZn
  • XXX 是“简单”的可行域,例如 0-1 变量或区间限制。

2. 构造拉格朗日函数:

将难处理的约束 Ax≥bAx \geq bAxb 松弛进目标函数,使用乘子 λ≥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(bAx)

注意这里 b−Axb - AxbAx违反约束的程度(slack)。


3. 得到对偶问题(Dual Problem):

λ≥0\lambda \geq 0λ0 最大化 xxx 最小化的拉格朗日函数

max⁡λ≥0  θ(λ)=min⁡x∈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θ(λ)=xXmin[cTx+λT(bAx)]

这是一个凸优化问题(即使原始问题是非凸的),便于求解。


4. 上下界关系:

由于对偶性原理:

  • Primal optimal≥Dual optimal\text{Primal optimal} \geq \text{Dual optimal}Primal optimalDual 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 背包问题(带额外耦合约束)

原问题:

min⁡cTxs.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.cTxaTxb(资源约束)dTxe(难约束)xi{0,1}

松弛掉第 2 条约束,构造对偶函数并优化乘子。

背包问题定义

最大化:

max⁡∑i=1nvixi \max \sum_{i=1}^{n} v_i x_i maxi=1nvixi

约束:

∑i=1nwixi≤C,xi∈{0,1} \sum_{i=1}^{n} w_i x_i \leq C,\quad x_i \in \{0, 1\} i=1nwixiC,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λ(wixiC)

对固定 λ,求解:

max⁡xi∈{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(bAx)
应用场景整数规划、调度、路径规划、背包问题等
求解策略对偶问题 + 次梯度优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云SLAM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值