Set Matrix Zeroes算法详解

本文介绍了一个利用递归实现的算法,用于处理矩阵中元素为0的情况,通过该算法可以将含有0元素的行和列全部置为0。算法效率高且空间复杂度低。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.

click to show follow up.

Follow up:
Did you use extra space?
A straight forward solution using O(mn) space is probably a bad idea.
A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?

题意:给定一个矩阵,若矩阵中有个元素为0,则将其所在行和列的所有元素置0

解法:采用递归完成,判断当前位置的行和列是否应该置0,然后递归下一行和下一列,直到最后,递归完成后,再对矩阵中的元素置0

    void setZeroesCore(vector<vector<int>>& matrix,int row,int col,int rows,int cols)
    {
        if(row>=rows||col>=cols)return;
        bool rowflag=false;
        bool colflag=false;

        for(int i=0;i<cols;i++)
        {
            if(matrix[row][i]==0)
            {
                rowflag=true;
                break;
            }
        }

        for(int i=0;i<rows;i++)
        {
            if(matrix[i][col]==0)
            {
                colflag=true;
                break;
            }
        }

        if(col<cols-1||row<rows-1)
        {
            int col1=col;
            int row1=row;
            if(col<cols-1)col1++;
            if(row<rows-1)row1++;
            setZeroesCore(matrix,row1,col1,rows,cols);
        }

        if(rowflag)
        {
            for(int i=0;i<cols;i++)
            {
                matrix[row][i]=0;
            }
        }
        if(colflag)
        {
            for(int i=0;i<rows;i++)
            {
                matrix[i][col]=0;
            }
        }
    }
    void setZeroes(vector<vector<int>>& matrix) {
        int m=matrix.size();
        if(m==0)return;
        int n=matrix[0].size();
        setZeroesCore(matrix,0,0,m,n);
    }
<think>我们正在讨论Active Set优化算法,这是一种用于解决带不等式约束的二次规划(Quadratic Programming, QP)问题的有效方法。该算法通过迭代地估计哪些约束是起作用的(即位于边界上,称为active set)来逐步求解。 ### Active Set方法的基本原理 Active Set方法的核心思想是:在每一步迭代中,假设已知当前起作用的约束集合(active set),然后在该集合对应的等式约束下求解一个子问题(即一个等式约束的二次规划)。如果该子问题的解满足所有约束且满足最优性条件,则算法终止;否则,调整active set(添加或移除约束)并继续迭代。 ### 算法流程图 由于用户要求流程图,我们使用mermaid语法来绘制。注意:由于文本环境限制,这里将用mermaid代码表示,用户可在支持mermaid的环境中渲染。 ```mermaid graph TD A[开始] --> B[初始化:给定初始可行点x0,确定初始active set A0] B --> C[迭代k:在当前active set Ak下求解等式约束子问题,得到步长p] C --> D{检查步长p} D -->|p=0| E[计算拉格朗日乘子] E --> F{所有乘子≥0?} F -->|是| G[找到最优解,结束] F -->|否| H[从active set中移除一个具有负乘子的约束] H --> C D -->|p≠0| I[计算最大步长α,使得x+αp不违反约束] I --> J[更新x: x = x + αp] J --> K{α<1?} K -->|是| L[将导致α<1的约束加入active set] L --> C K -->|否| C ``` ### 实现细节 1. **初始化**:需要一个初始可行点$x_0$。如果问题没有显式给出,可以通过求解一个辅助问题(如线性规划)来获得。初始active set $A_0$通常取$x_0$处所有起作用的约束(包括等式约束和不等式约束中取等号的部分)。 2. **求解等式约束子问题**:在active set $A_k$下,问题变为: $$\min \frac{1}{2}x^T Q x + c^T x \quad \text{s.t.} \quad a_i^T x = b_i, i \in A_k$$ 这是一个等式约束的二次规划,可以用拉格朗日乘子法或直接求解KKT系统(线性方程组)来得到解$x_k$和拉格朗日乘子$\lambda_k$。 3. **判断步长**:如果从当前点$x_k$到子问题解$d_k$(即步长)为零,则检查拉格朗日乘子: - 如果所有乘子$\lambda_i \geq 0$(对于不等式约束),则当前点为原问题的最优解。 - 否则,移除一个具有负乘子的约束(通常选择最负的那个),然后重新求解子问题。 4. **步长不为零**:计算最大步长$\alpha$,使得$x_k + \alpha d_k$不违反任何约束。即: $$\alpha = \min \left\{ 1, \min_{j \notin A_k, a_j^T d_k >0} \frac{b_j - a_j^T x_k}{a_j^T d_k} \right\}$$ 这里,$a_j$和$b_j$对应不等式约束$a_j^T x \leq b_j$。如果$\alpha<1$,说明遇到了新的约束,需要将该约束加入active set。 5. **更新**:令$x_{k+1} = x_k + \alpha d_k$,并更新active set(如果$\alpha<1$则添加新约束)。 6. **终止条件**:当步长为零且所有乘子非负时终止。 ### 代码实现(Python伪代码) 以下是一个简化的Active Set算法实现框架(假设问题为凸二次规划): ```python import numpy as np from scipy.linalg import solve def active_set_qp(Q, c, A_ineq, b_ineq, A_eq=None, b_eq=None, x0=None, max_iter=100): # 初始化:这里假设已经有一个初始可行点x0,如果没有,需要先计算一个可行点(略) n = Q.shape[0] x = x0.copy() # 初始active set:存储起作用的不等式约束的索引(等式约束始终在active set中) active_set = set() # 将等式约束加入active set(如果有的话) # 注意:我们假设A_ineq和b_ineq对应不等式约束,A_eq和b_eq对应等式约束 for k in range(max_iter): # 构建当前active set对应的等式约束矩阵(包括等式约束和active_set中的不等式约束) A_active = [] b_active = [] if A_eq is not None: A_active.append(A_eq) b_active.append(b_eq) for idx in active_set: A_active.append(A_ineq[idx:idx+1, :]) b_active.append(b_ineq[idx:idx+1]) if len(A_active) == 0: # 没有等式约束,则直接求无约束极值点 p = -np.linalg.solve(Q, c) else: A_active = np.vstack(A_active) b_active = np.hstack(b_active) # 构建KKT系统(等式约束二次规划的KKT条件) KKT_matrix = np.block([[Q, A_active.T], [A_active, np.zeros((A_active.shape[0], A_active.shape[0]))]]) rhs = np.hstack([-c, b_active - A_active.dot(x)]) # 注意:子问题是在当前active set下求方向p,使得x+p满足等式,且使目标函数最小 # 求解KKT系统 sol = solve(KKT_matrix, rhs) p = sol[:n] # 步长 lam = sol[n:] # 拉格朗日乘子(包括等式约束和active_set中的不等式约束) # 检查步长p是否为0(或足够小) if np.linalg.norm(p) < 1e-10: # 检查不等式约束对应的乘子(注意:拉格朗日乘子中,等式约束部分可能为负,但不等式约束部分必须非负) # 我们只关心active_set中不等式约束的乘子(在lam中,等式约束的乘子在前,然后是不等式约束的乘子) # 假设lam中前len(b_eq)个是等式约束的乘子,后面的是不等式约束的乘子(active_set中的) # 注意:等式约束的乘子可正可负,所以只检查不等式约束部分 if len(active_set) > 0: # 从lam中取出不等式约束对应的乘子(注意:lam中先等式后不等式,且按active_set加入的顺序) eq_count = A_eq.shape[0] if A_eq is not None else 0 ineq_lam = lam[eq_count:eq_count+len(active_set)] min_lam = np.min(ineq_lam) if min_lam >= -1e-10: # 非负,则最优解 break else: # 移除具有最小(最负)乘子的约束 idx_to_remove = np.argmin(ineq_lam) # 将active_set中对应的约束移除(注意:active_set是集合,我们将其转为有序列表以便索引) active_set_list = list(active_set) active_set.remove(active_set_list[idx_to_remove]) else: # 没有active_set中的不等式约束,且步长为0,则已是最优 break else: # 计算最大步长alpha(防止违反非active的约束) alpha = 1.0 blocking_idx = -1 # 检查不在active_set中的不等式约束 for i in range(A_ineq.shape[0]): if i in active_set: continue a_i = A_ineq[i] b_i = b_ineq[i] denom = a_i.dot(p) if denom > 1e-10: # 避免除以0或负数(负数表示不会违反) # 计算该约束允许的最大步长 alpha_i = (b_i - a_i.dot(x)) / denom if alpha_i < alpha: alpha = alpha_i blocking_idx = i # 更新x x = x + alpha * p # 如果alpha<1,则添加新的约束到active_set if alpha < 1 - 1e-10 and blocking_idx != -1: active_set.add(blocking_idx) return x ``` 注意:上述代码是简化版,实际应用中需要处理更多细节(如初始可行点的获取,数值稳定性,等式约束的处理等)。此外,对于非凸问题,该算法可能陷入局部最优。 ### 相关引用 Active Set方法在优化领域有广泛应用,尤其在二次规划中[^1]。它通过调整约束集来逼近最优解,类似于梯度下降法在约束边界上的探索。 §§相关问题§§ 1. Active Set方法与其他约束优化算法(如内点法)相比有什么优缺点? 2. 如何获得Active Set算法的初始可行点? 3. Active Set方法在非凸二次规划中是否适用?</think>### Active Set优化算法详解 #### 算法原理 Active Set方法是一种用于求解带不等式约束的二次规划(QP)问题的迭代算法: $$\min_x \frac{1}{2}x^T Q x + c^T x \quad \text{s.t.} \quad \begin{cases} A_{eq}x = b_{eq} \\ A_{ineq}x \leq b_{ineq} \end{cases}$$ 其核心思想是通过动态调整活动约束集(即等式约束和紧致不等式约束)来逼近最优解。算法在每次迭代中: 1. 在当前活动约束下求解等式约束子问题 2. 检查最优性条件(KKT条件) 3. 根据结果添加或移除约束[^1] #### 算法流程图 ```mermaid graph TD A[开始] --> B[初始化可行点 x0 和活动集 A] B --> C[求解当前活动集下的等式约束子问题] C --> D{解是否可行?} D -->|是| E[检查KKT条件] D -->|否| F[沿可行方向移动] F --> G{是否遇到新约束?} G -->|是| H[添加新约束到活动集] G -->|否| C E --> I{所有乘子 ≥0?} I -->|是| J[输出最优解] I -->|否| K[移除负乘子对应约束] K --> C J --> L[结束] ``` #### 关键步骤实现细节 1. **初始化** - 寻找初始可行点$x_0$(可能需辅助线性规划) - 初始化活动集$A$为$x_0$处的紧致约束:$A = \{ i | a_i^T x_0 = b_i \}$ 2. **等式约束子问题求解** $$\min_p \frac{1}{2}p^T Q p + (Qx_k + c)^T p$$ $$\text{s.t. } a_i^T p = 0, \quad \forall i \in A$$ 通过KKT系统求解: $$ \begin{bmatrix} Q & A_A^T \\ A_A & 0 \end{bmatrix} \begin{bmatrix} p \\ \lambda \end{bmatrix} = \begin{bmatrix} -(Qx_k + c) \\ 0 \end{bmatrix} $$ 3. **约束管理** - **添加约束**:当步长$\alpha < 1$时,添加阻挡约束: $$\alpha = \min_{j \notin A} \left( \frac{b_j - a_j^T x_k}{a_j^T p} \right) \quad (a_j^T p > 0)$$ - **移除约束**:当$\lambda_i < 0$时,移除对应不等式约束 4. **终止条件** - 步长$p=0$且所有乘子$\lambda_i \geq 0$ - 达到最大迭代次数 #### Python实现示例 ```python import numpy as np from scipy.linalg import solve def active_set_qp(Q, c, A_ineq, b_ineq, max_iter=100, tol=1e-6): n = Q.shape[0] x = np.zeros(n) # 初始点(需可行性处理) active_set = set() # 活动约束索引 for _ in range(max_iter): # 构建活动约束矩阵 A_active = [] b_active = [] for i in active_set: A_active.append(A_ineq[i]) b_active.append(b_ineq[i]) if A_active: A_active = np.array(A_active) b_active = np.array(b_active) # 构造KKT系统 KKT = np.block([[Q, A_active.T], [A_active, np.zeros((len(active_set), len(active_set)))]]) rhs = np.concatenate([-Q.dot(x)-c, -A_active.dot(x)+b_active]) # 求解方向 step = solve(KKT, rhs, assume_a='sym')[:n] else: step = -np.linalg.solve(Q, Q.dot(x)+c) # 检查收敛 if np.linalg.norm(step) < tol: # 计算乘子并检查最优性 lambdas = solve(A_active, Q.dot(x)+c)[:len(active_set)] if all(l >= -tol for l in lambdas): break # 移除负乘子约束 idx_remove = np.argmin(lambdas) active_set.remove(list(active_set)[idx_remove]) continue # 计算步长 alpha = 1.0 blocking_idx = -1 for i in range(len(b_ineq)): if i in active_set: continue denom = A_ineq[i].dot(step) if denom > tol: alpha_i = (b_ineq[i] - A_ineq[i].dot(x)) / denom if alpha_i < alpha: alpha = alpha_i blocking_idx = i # 更新解 x = x + alpha * step # 添加新约束 if alpha < 1 and blocking_idx != -1: active_set.add(blocking_idx) return x ``` #### 应用场景 Active Set算法特别适用于: 1. 模型预测控制(MPC)中的实时优化 2. 支持向量机(SVM)训练 3. 投资组合优化 4. 机械臂轨迹规划[^1] #### 性能特点 - **优点**:超线性收敛,内存效率高 - **缺点**:初始点敏感,最坏情况指数时间复杂度 - **改进方向**:与内点法结合,启发式初始点选择
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值