主页 模型库 竞赛 学习中心 工具箱 论文库 前沿 建模空间 AI建模
学习中心 探索专题 数学规划与优化算法 整数规划

本页实验台

  1. 松弛与取整的陷阱
  2. 分支定界树
  3. 割平面
  4. 0-1 背包与指派
LAB 01

松弛与取整的陷阱

线性松弛的解四舍五入,为什么常常既不可行也不最优

问题
买 2.7 台机器是没有意义的。那把线性规划的解四舍五入不就行了?这一台展示为什么不行:取整之后的点可能不可行,即使可行也可能离最优很远,而且维数越高偏得越离谱。
模型
整数规划与它的线性松弛。
IP: max cTx, Ax ≤ b, x ∈ ℤn≥0 ⊂ LP 松弛: x ∈ ℝn≥0
整数间隙 gap = zLP − zIP ≥ 0
LP 松弛的最优值永远是 IP 的一个上界(最大化时),这是分支定界能剪枝的全部依据。而当约束矩阵是全幺模的(如指派、网络流),松弛解自动是整数——那时候松弛就是精确解。
LP 松弛最优—
四舍五入之后—
真正的整数最优—
松弛上界与整数最优的差—
默认参数下,松弛解是 (3, 1.5),四舍五入成 (3, 2)——不可行。退一步取 (3, 1),可行,目标值 19。但真正的整数最优是 (4, 0),目标值 20,它离松弛解足足有一个格子远,靠"就近取整"永远也找不到。这就是整数规划最容易被低估的地方:松弛解只是一个上界,它的位置并不指示整数最优在哪里。把 b₂ 拖到 8,四舍五入恰好就是最优;拖到 11,取整只能退到 (0, 5) 得 20,而真正的最优是 (2, 3) 得 22,差了 10%。两种情况看起来没有任何区别,而你事先无法知道自己在哪一种里。顺带一提,如果约束矩阵是全幺模的(比如网络流、指派问题),松弛解本身就一定是整数,那时候才真的可以"解完就用"。
LAB 02

分支定界树

看搜索树怎么长、界怎么剪枝,剪掉了多少节点

问题
整数规划是 NP-难的,可求解器却能在几秒内解掉上万个变量的问题。它靠的不是穷举,而是"证明大部分分支不用看"。这一台把那棵搜索树画出来,看界是怎么剪掉它的。
模型
分支定界:分支制造子问题,定界证明子问题不用看。
分支:xj ≤ ⌊x̄j⌋ 与 xj ≥ ⌈x̄j⌉;  剪枝:zLP(节点) ≤ 当前最好整数解 ⟹ 整枝丢弃
剪枝的效率取决于界有多紧——所以现代求解器把大量力气花在"让松弛更紧"上(割平面、预处理)。这一台的"按什么顺序搜"滑杆能直接看到搜索策略对树大小的影响。
已展开 / 总共展开的节点数—
被剪掉的节点数—
当前最好整数解—
当前全局上界—
把"用界剪枝"关掉,看节点数怎么变。默认这组数据(10 件物品):最佳优先 + 剪枝,一共只展开 33 个节点就拿到最优值 94;关掉剪枝,同样的搜索要展开 1050 个——差 32 倍。把物品数拉到 14 或 16,不剪枝那一栏会直接撞上 4000 个节点的上限(读数后面出现 ⚑),而剪枝那一栏仍然只要三四十个。剪枝的逻辑只有一句话:如果这个子问题的上界还不如手上已有的整数解好,它下面所有分支都不用看了。这一句话就是分支定界能在实际问题上跑得动的全部原因——它并没有改变最坏情况的复杂度,只是让绝大多数分支根本没机会展开。也正因为这样,先找到一个好的可行解非常重要:手上的整数解越好,剪掉的分支就越多。工程上常见的做法是先用启发式跑出一个不错的解喂给求解器,这往往比调求解器参数管用得多。最后对比两种搜索顺序:同样开着剪枝,最佳优先 33 个节点,深度优先要 358 个;但深度优先内存占用小、能更快拿到第一个可行解。真实求解器两者混用。
LAB 03

割平面

一刀一刀削掉非整数顶点,可行域慢慢逼近整数壳

问题
与其在搜索树上硬拼,不如把可行域本身削得更接近整数点——削掉那些只含分数解的角。这就是割平面,戈莫里 1958 年的想法,也是今天求解器速度的一半来源。
模型
割平面:加入对所有整数可行解成立、但割掉当前分数最优解的不等式。
对任意整数解满足 aTx ≤ β,而当前 LP 最优 x̄ 满足 aTx̄ > β;  目标是逼近整数壳 conv(XIP)
整数壳(所有整数可行解的凸包)上做 LP 就直接得到 IP 的解,但整数壳的不等式数量一般是指数的。实践中只加"有用的"几刀,和分支定界配合成分支切割法。
当前松弛最优值—
当前松弛最优解—
是不是整数解了—
离整数最优还差多少—
每加一刀,可行域就削掉一块,但一个整数点都不会被削掉。这是割平面法的核心要求:割掉的必须只有分数顶点。Gomory 割是从单纯形表里直接读出来的——取一行基变量取值为分数的方程,把所有系数取小数部分,利用"整数的非负组合还是整数"就能推出一条有效不等式。勾上"画出整数壳":那个由所有可行整数点围成的最小凸多边形,就是理论上的终点。如果一开始就知道整数壳,整数规划就退化成线性规划了——可惜它的面数一般是指数级的,所以只能一刀一刀地逼近。实践中割平面很少单独用,而是和分支定界合成"分支切割",这是今天所有商用求解器的主力算法。
LAB 04

0-1 背包与指派

两个最常考的模型,动态规划与匈牙利算法的解法对照

问题
背包和指派是建模里出现频率最高的两个模板。有意思的是:一个只能靠动态规划(而且是伪多项式的),另一个却有多项式的精确算法。差别在哪儿?在约束矩阵的结构。
模型
两个模型的标准写法。
背包:max Σ vixi,  Σ wixi ≤ W,  x ∈ {0,1}n → DP O(nW)
指派:min Σ cijxij,  行列和为 1 → 匈牙利算法 O(n³)
指派问题的约束矩阵是全幺模的,所以 LP 松弛自动整数;背包不是,它的 DP 复杂度含 W,是伪多项式——W 用二进制编码时长度是 log W,所以这仍然是指数的。
精确解的目标值—
贪心解的目标值—
贪心差了多少—
与暴力枚举的对照—
这两个模型都是整数规划,但都不用分支定界。0-1 背包有伪多项式的动态规划(O(nW)),指派问题有匈牙利算法(O(n³));指派问题之所以这么好解,是因为它的约束矩阵全幺模,线性松弛的最优解天然就是 0-1 的。再看贪心:背包按"性价比"排序装,通常很接近但不保证最优;指派按"每人挑当前最便宜的任务"更容易吃亏。换几组数据,看贪心什么时候失手。最后那个读数是自检——两种算法的结果都拿去和暴力枚举对过,这是写优化代码时最值得养成的习惯:小规模上一定要有一个"笨但一定对"的参照。

这一页想说的

整数规划的难,不在于"多了一个整数条件",而在于这个条件把凸性毁掉了。所有的技术——分支、定界、割平面、启发式初始解——都是在想办法把非凸问题重新装回一串凸问题里去解。建模时最实际的两条建议是:一、能用连续变量就别用整数变量,能用 0-1 就别用一般整数;二、花时间把模型写紧(更强的不等式、更少的对称性),往往比换一个更贵的求解器有用得多。

接着看

← 返回模块 ← 返回探索专题