二次规划问题的KKT 条件求解方法

专栏文章汇总


文章结构如下:

1: 等式约束优化问题

2: 不等式约束优化问题

3: 一个例子


注:本文来自台湾周志成老师《线性代数》及其博客

Karush-Kuhn-Tucker (KKT)条件是非线性规划(nonlinear programming)最佳解的必要条件。KKT条件将Lagrange乘数法(Lagrange multipliers)所处理涉及等式的约束优化问题推广至不等式。在实际应用上,KKT条件(方程组)一般不存在代数解,许多优化算法可供数值计算选用。这篇短文从Lagrange乘数法推导KKT条件并举一个简单的例子说明解法。


1: 等式约束优化问题

给定一个目标函数 [公式] ,我们希望找到 [公式] ,在满足约束条件 [公式] 的前提下,使得 [公式] 有最小值。这个约束优化问题记为

[公式]
为方便分析,假设 [公式][公式] 是连续可导函数。Lagrange乘数法是等式约束优化问题的典型解法。定义Lagrangian函数

[公式]
其中 [公式] 称为Lagrange乘数。Lagrange乘数法将原本的约束优化问题转换成等价的无约束优化问题

[公式]
计算 [公式][公式][公式] 的偏导数并设为零,可得最优解的必要条件:

[公式]
其中第一式为定常方程式(stationary equation),第二式为约束条件。解开上面 [公式] 个方程式可得 [公式] 的stationary point [公式] 以及 [公式] 的值(正负数皆可能)。


2: 不等式约束优化问题

接下来我们将约束等式 [公式] 推广为不等式 [公式] 。考虑这个问题

[公式]
约束不等式 [公式] 称为原始可行性(primal feasibility),据此我们定义可行域(feasible region) [公式] 。假设 [公式] 为满足约束条件的最佳解,分开两种情况讨论:

(1) [公式] ,最佳解位于 [公式] 的内部,称为内部解(interior solution),这时约束条件是无效的(inactive);

(2) [公式] ,最佳解落在 [公式] 的边界,称为边界解(boundary solution),此时约束条件是有效的(active)。

这两种情况的最佳解具有不同的必要条件。

(1)内部解:在约束条件无效的情形下, [公式] 不起作用,约束优化问题退化为无约束优化问题,因此驻点 [公式] 满足 [公式][公式]

(2)边界解:在约束条件有效的情形下,约束不等式变成等式 [公式] ,这与前述Lagrange乘数法的情况相同。我们可以证明驻点 [公式] 发生于 [公式] ,换句话说,存在 [公式] 使得 [公式] ,但这里 [公式] 的正负号是有其意义的。因为我们希望最小化 [公式] ,梯度 [公式] (函数 [公式] 在点 [公式] 的最陡上升方向)应该指向可行域 [公式] 的内部(因为你的最优解最小值是在边界取得的),但 [公式] 指向 [公式] 的外部(即 [公式] 的区域,因为你的约束是小于等于0),因此 [公式] ,称为对偶可行性(dual feasibility)

因此,不论是内部解或边界解, [公式] 恒成立,称为互补松弛性(complementary slackness)。整合上述两种情况,最佳解的必要条件包括Lagrangian函数 [公式] 的定常方程式、原始可行性、对偶可行性,以及互补松弛性:

[公式]
这些条件合称为Karush-Kuhn-Tucker (KKT)条件。如果我们要最大化 [公式] 且受限于 [公式] ,那么对偶可行性要改成 [公式]

上面结果可推广至多个约束等式与约束不等式的情况。考虑标准约束优化问题(或称非线性规划):

[公式]

定义Lagrangian 函数

[公式]
其中 [公式] 是对应 [公式] 的Lagrange乘数, [公式] $是对应 [公式] 的Lagrange乘数(或称KKT乘数)。KKT条件包括

[公式]

注:感谢评论区 追梦的lin 提出,在使用KKT条件时需要满足Regularity conditions (or constraint qualifications),维基在第三部分有了介绍: Karush-Kuhn-Tucker conditions 。比较常见的是Linearity constraint qualification (LCQ),即约束条件是仿射函数。

3: 一个例子

考虑这个问题

[公式]
其中 [公式] 为实数。写出Lagrangigan函数

[公式]
KKT 方程组如下:

[公式]
求偏导可得 [公式][公式] ,分别解出 [公式][公式] 。代入约束等式 [公式][公式] 。合并上面结果,

[公式]
最后再加入约束不等式 [公式][公式] 。底下分开三种情况讨论。

(1) [公式] :不难验证 [公式] 满足所有的KKT条件,约束不等式是无效的, [公式] 是内部解,目标函数的极小值是 [公式]

(2) [公式] :如同1, [公式] 满足所有的KKT条件, [公式] 是边界解,因为 [公式]

(3) [公式] :这时约束不等式是有效的, [公式] ,则 [公式][公式] ,目标函数的极小值是 [公式]


4: 参考文献

周志成:《线性代数》,国立交通大学出版社

Karush-Kuhn-Tucker (KKT) 條件

  • 19
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于Java的二次规划算法代码,用于求解带有不等式约束的二次规划问题: ``` import java.util.Arrays; public class QuadraticProgramming { public static void main(String[] args) { double[][] Q = {{1, 0}, {0, 1}}; // 二次项系数矩阵 double[] c = {0, 0}; // 一次项系数向量 double[][] A = {{-1, 0}, {0, -1}, {1, 1}}; // 不等式约束系数矩阵 double[] b = {-1, -1, 2}; // 不等式约束右端向量 double[] x = solve(Q, c, A, b); // 求解 System.out.println(Arrays.toString(x)); } public static double[] solve(double[][] Q, double[] c, double[][] A, double[] b) { int n = Q.length; int m = A.length; double[][] P = new double[n + m][n + m]; double[] q = new double[n + m]; double[] lb = new double[n + m]; Arrays.fill(lb, Double.NEGATIVE_INFINITY); // 所有变量下界为负无穷 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { P[i][j] = Q[i][j]; } q[i] = c[i]; } for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { P[n + i][j] = A[i][j]; P[j][n + i] = A[i][j]; } q[n + i] = b[i]; } double[] x = QPSolver.solveQP(P, q, lb, null); // 调用二次规划求解器 return Arrays.copyOf(x, n); } } class QPSolver { private static final double EPS = 1e-8; private static int[] initActiveSet(double[][] A, double[] b, int[] activeSet) { int m = A.length; if (activeSet == null) { activeSet = new int[m]; for (int i = 0; i < m; i++) { activeSet[i] = i; } } int[] newActiveSet = new int[m]; int cnt = 0; for (int i = 0; i < m; i++) { double s = 0; for (int j = 0; j < m; j++) { s += A[j][activeSet[i]] * b[j]; } if (Math.abs(s) < EPS) { newActiveSet[cnt++] = activeSet[i]; } } return Arrays.copyOf(newActiveSet, cnt); } private static boolean isFeasible(double[][] A, double[] b, double[] x) { int m = A.length; for (int i = 0; i < m; i++) { double s = 0; for (int j = 0; j < x.length; j++) { s += A[i][j] * x[j]; } if (s > b[i] + EPS) { return false; } } return true; } public static double[] solveQP(double[][] P, double[] q, double[] lb, double[] ub) { int n = P.length; int m = n; double[][] A = new double[m][n]; double[] b = new double[m]; for (int i = 0; i < n; i++) { A[i][i] = -1; if (lb[i] > Double.NEGATIVE_INFINITY) { A[n][i] = 1; b[n++] = lb[i]; } if (ub != null && ub[i] < Double.POSITIVE_INFINITY) { A[n][i] = -1; b[n++] = -ub[i]; } } int[] activeSet = null; while (true) { activeSet = initActiveSet(A, b, activeSet); double[][] G = new double[activeSet.length][activeSet.length]; double[] d = new double[activeSet.length]; for (int i = 0; i < activeSet.length; i++) { for (int j = 0; j < activeSet.length; j++) { G[i][j] = P[activeSet[i]][activeSet[j]]; } d[i] = q[activeSet[i]]; } double[] x = solveQP(G, d); if (x == null) { return null; } double alpha = Double.POSITIVE_INFINITY; int alphaIndex = -1; for (int i = 0; i < activeSet.length; i++) { if (x[i] < -EPS) { double t = (b[activeSet[i]] - A[activeSet[i]][activeSet.length] * x[activeSet.length]) / (A[activeSet[i]][i] - A[activeSet[i]][activeSet.length] * x[activeSet.length]); if (t < alpha) { alpha = t; alphaIndex = i; } } if (x[i] > EPS && ub == null) { double t = (b[activeSet[i]] - A[activeSet[i]][activeSet.length] * x[activeSet.length]) / (A[activeSet[i]][i] - A[activeSet[i]][activeSet.length] * x[activeSet.length]); if (t < alpha) { alpha = t; alphaIndex = i; } } } if (alpha == Double.POSITIVE_INFINITY) { return Arrays.copyOf(x, n); } double[] y = Arrays.copyOf(x, activeSet.length + 1); y[y.length - 1] = alpha; Arrays.sort(y); int j = 0; while (y[j] < 0) { j++; } if (j == y.length - 1 || y[j] >= alpha) { j--; } double[] newActiveSet = new double[activeSet.length - 1]; int cnt = 0; for (int i = 0; i < activeSet.length; i++) { if (i != alphaIndex) { newActiveSet[cnt++] = activeSet[i]; } } newActiveSet[cnt++] = alphaIndex; Arrays.sort(newActiveSet); double[][] newA = new double[m][cnt]; double[] newB = new double[m]; for (int i = 0; i < m; i++) { double s = 0; for (int k = 0; k < cnt - 1; k++) { newA[i][k] = A[i][(int) newActiveSet[k]]; s += A[i][(int) newActiveSet[k]] * x[(int) newActiveSet[k]]; } newA[i][cnt - 1] = A[i][n - 1]; newB[i] = b[i] - s; } if (isFeasible(newA, newB, x)) { A = newA; b = newB; activeSet = Arrays.copyOf(newActiveSet, cnt); } } } private static double[] solveQP(double[][] G, double[] d) { int n = G.length; double[] x = new double[n]; Arrays.fill(x, Double.POSITIVE_INFINITY); boolean feasible = true; for (int i = 0; i < n; i++) { if (G[i][i] <= 0) { feasible = false; break; } } if (feasible) { double[][] L = new double[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { double s = 0; for (int k = 0; k < j; k++) { s += L[i][k] * L[j][k]; } L[i][j] = (G[i][j] - s) / L[j][j]; } double s = 0; for (int k = 0; k < i; k++) { s += L[i][k] * L[i][k]; } L[i][i] = Math.sqrt(G[i][i] - s); } double[] z = new double[n]; for (int i = 0; i < n; i++) { double s = d[i]; for (int j = 0; j < i; j++) { s -= L[i][j] * z[j]; } z[i] = s / L[i][i]; } for (int i = n - 1; i >= 0; i--) { double s = z[i]; for (int j = i + 1; j < n; j++) { s -= L[j][i] * x[j]; } x[i] = s / L[i][i]; } } return feasible ? x : null; } } ``` 在上面的代码中,我们使用了一个二次规划求解器 `QPSolver`,它可以求解带有不等式约束的二次规划问题。具体来说,我们将带有不等式约束的二次规划问题转化为一个等式约束的二次规划问题,并使用 `QPSolver` 求解。在转化过程中,我们将所有变量的下界设为负无穷,所有变量的上界设为正无穷,对于每个不等式约束 $a^\top x \leq b$,我们引入一个松弛变量 $t$,将其转化为等式约束 $a^\top x + t = b$,然后将松弛变量 $t$ 的下界设为 $0$。在求解等式约束的二次规划问题时,我们使用了 Cholesky 分解法。 使用以上代码,我们可以求解任意带有不等式约束的二次规划问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值