0

0

使用 NumPy 和 SciPy 解决带线性约束的线性方程组

心靈之曲

心靈之曲

发布时间:2025-09-30 15:29:02

|

607人浏览过

|

来源于php中文网

原创

使用 numpy 和 scipy 解决带线性约束的线性方程组

本文探讨了如何在存在线性约束的情况下,有效求解线性方程组 AX=b。通过对比基于优化的 scipy.optimize.minimize 方法与直接的 np.linalg.lstsq 最小二乘法,阐明了将线性约束整合到方程组中并使用最小二乘求解器是处理此类问题的更优选择,尤其适用于寻求精确或最佳拟合解的场景。

引言:带约束的线性方程组求解挑战

在科学和工程计算中,我们经常需要解决形如 AX=b 的线性方程组,其中 A 是系数矩阵,X 是未知数向量,b 是常数向量。然而,实际问题往往伴随着对 X 中元素的一些额外限制,即约束条件。当这些约束是线性的时候,如何有效地将它们融入到求解过程中,并找到一个既满足原始方程组又符合所有约束的解,是一个常见的挑战。

一个常见的误区是尝试将约束条件作为惩罚项或通过优化方法来解决。虽然优化方法(如 scipy.optimize.minimize)可以处理约束,但如果目标是精确求解 AX=b 并在满足约束的同时最小化残差,那么直接的最小二乘法结合系统增广往往是更简洁和高效的方案。

传统优化方法的局限性

考虑一个典型的场景:我们有一个 8x8 的矩阵 A 和 8x1 的向量 b,需要求解 X。同时,X 的元素之间存在以下线性约束:

  1. 0.5 * (y1 + y2) = 0
  2. 0.5 * (x3 + x4) = 0
  3. 0.5 * (y3 + y4) = 0

其中 X = [x1, y1, x2, y2, x3, y3, x4, y4]。

如果尝试使用 scipy.optimize.minimize,我们通常会定义一个目标函数来最小化 ||AX - b||^2(即残差的平方和),并将约束作为等式约束传递给优化器。

import numpy as np
from scipy import optimize

# 示例数据
A = np.array([
    [-261.60,  11.26,      0.0,    0.0,     0.0,    0.0,     0.0,    0.0],
    [   4.07, -12.75,      0.0,    0.0,     0.0,    0.0,     0.0,    0.0],
    [    0.0,    0.0,  -158.63,  -5.65,     0.0,    0.0,     0.0,    0.0],
    [    0.0,    0.0,    -2.81, -12.14,     0.0,    0.0,     0.0,    0.0],
    [    0.0,    0.0,      0.0,    0.0, -265.99,  19.29,     0.0,    0.0],
    [    0.0,    0.0,      0.0,    0.0,   12.59, -12.34,     0.0,    0.0],
    [    0.0,    0.0,      0.0,    0.0,     0.0,    0.0, -166.25, -12.63],
    [    0.0,    0.0,      0.0,    0.0,     0.0,    0.0,   -8.40, -11.14]
])

b = np.array([
     -6.95,
     16.35,
     -0.96,
     16.35,
     19.19,
    -15.85,
    -12.36,
    -15.63]).reshape(-1, 1)

def objective_function(x):
    """目标函数:最小化 (AX - b) 的L2范数平方"""
    return np.sum((np.dot(A, x) - b.flatten())**2)

def constraints(x):
    """线性等式约束函数"""
    # X = [x1, y1, x2, y2, x3, y3, x4, y4]
    # 索引: x[0]=x1, x[1]=y1, x[2]=x2, x[3]=y2, x[4]=x3, x[5]=y3, x[6]=x4, x[7]=y4
    return np.array([
        0.5 * (x[1] + x[3]),  # 0.5*(y1 + y2) = 0
        0.5 * (x[4] + x[6]),  # 0.5*(x3 + x4) = 0
        0.5 * (x[5] + x[7])   # 0.5*(y3 + y4) = 0
    ])

cons = {'type': 'eq', 'fun': constraints}
res = optimize.minimize(objective_function, np.zeros(A.shape[1]), method='SLSQP', constraints=cons)

x_optimized = res.x

print("优化器找到的解 X:")
print(x_optimized)
print("\n验证约束条件 (应接近于0):")
print(constraints(x_optimized))
print("\n验证 AX 与 b 的匹配程度:")
print(np.matmul(A, x_optimized).reshape(-1, 1))
print("\n期望的 b 向量:")
print(b)

运行上述代码,会发现优化器虽然成功地使约束条件接近于零,但 np.matmul(A, x_optimized) 的结果与原始 b 向量仍存在显著差异。这是因为 minimize 函数的目标是找到一个 X,使得目标函数(即 ||AX - b||^2)在满足约束的前提下达到最小值。如果原始系统 AX=b 在满足约束的情况下本身就没有精确解(即 ||AX - b||^2 的最小值为非零),那么 minimize 就不会强制 AX 等于 b,而是找到一个残差最小的解。

增广系统与最小二乘法:更直接的解决方案

对于线性约束,存在一种更直接、更符合数学原理的方法:将约束条件直接整合到原始的线性方程组中,形成一个增广系统,然后使用最小二乘法求解。

每个线性约束 c_1 x_1 + c_2 x_2 + ... + c_n x_n = d 都可以被视为原始系统的一个额外方程。通过将这些约束方程添加到 A 矩阵和 b 向量中,我们构建了一个新的、可能过定(方程数多于未知数)的线性系统 A_aug X = b_aug。然后,我们可以使用 np.linalg.lstsq 来求解这个增广系统,它会找到一个 X,使得 ||A_aug X - b_aug||^2 最小。如果增广系统有精确解,lstsq 将返回该精确解。

步骤:

千博购物系统.Net
千博购物系统.Net

千博购物系统.Net能够适合不同类型商品,为您提供了一个完整的在线开店解决方案。千博购物系统.Net除了拥有一般网上商店系统所具有的所有功能,还拥有着其它网店系统没有的许多超强功能。千博购物系统.Net适合中小企业和个人快速构建个性化的网上商店。强劲、安全、稳定、易用、免费是它的主要特性。系统由C#及Access/MS SQL开发,是B/S(浏览器/服务器)结构Asp.Net程序。多种独创的技术使

下载
  1. 将约束表示为矩阵形式 C X = d。 对于给定的约束:

    • 0.5 * y1 + 0.5 * y2 = 0
    • 0.5 * x3 + 0.5 * x4 = 0
    • 0.5 * y3 + 0.5 * y4 = 0

    我们可以构建一个 3x8 的矩阵 C 和一个 3x1 的向量 d。 X = [x1, y1, x2, y2, x3, y3, x4, y4]

    C 矩阵的行对应每个约束,列对应 X 中的变量: C = [[0, 0.5, 0, 0.5, 0, 0, 0, 0],[0, 0, 0, 0, 0.5, 0, 0.5, 0],[0, 0, 0, 0, 0, 0.5, 0, 0.5]]d = [[0], [0], [0]]

  2. 增广 A 和 b。 将 C 垂直堆叠到 A 下方,将 d 垂直堆叠到 b 下方。

    A_aug = np.vstack([A, C])b_aug = np.vstack([b, d])

  3. 使用 np.linalg.lstsq 求解。np.linalg.lstsq(A_aug, b_aug, rcond=None) 将返回增广系统的最小二乘解。

# 原始 A 和 b (与上文相同)
# A = ...
# b = ...

# 1. 构建约束矩阵 AC 和约束向量 bC
AC = np.zeros([3, A.shape[1]]) # 3个约束,8个变量
bC = np.zeros((3, 1))

# 填充 AC 矩阵
# X = [x1, y1, x2, y2, x3, y3, x4, y4]
# 索引: x[0]=x1, x[1]=y1, x[2]=x2, x[3]=y2, x[4]=x3, x[5]=y3, x[6]=x4, x[7]=y4

# 约束 1: 0.5*(y1 + y2) = 0  => 0.5*x[1] + 0.5*x[3] = 0
AC[0][[1, 3]] = 0.5

# 约束 2: 0.5*(x3 + x4) = 0  => 0.5*x[4] + 0.5*x[6] = 0
AC[1][[4, 6]] = 0.5

# 约束 3: 0.5*(y3 + y4) = 0  => 0.5*x[5] + 0.5*x[7] = 0
AC[2][[5, 7]] = 0.5

# bC 向量已初始化为零

# 2. 增广系统
A_augmented = np.vstack([A, AC])
b_augmented = np.vstack([b, bC])

print("增广后的 A 矩阵形状:", A_augmented.shape)
print("增广后的 b 向量形状:", b_augmented.shape)

# 3. 使用 np.linalg.lstsq 求解增广系统
# rcond=None 禁用 rcond 警告
x_lstsq, residuals, rank, singular_values = np.linalg.lstsq(A_augmented, b_augmented, rcond=None)

print("\nnp.linalg.lstsq 找到的解 X:")
print(x_lstsq.flatten())

# 验证约束条件
print("\n验证约束条件 (应接近于0):")
# 注意:x_lstsq 是一个列向量,需要展平或适当索引
print(np.dot(AC, x_lstsq).flatten())

# 验证原始 AX 与 b 的匹配程度
print("\n验证原始 AX 与 b 的匹配程度:")
print(np.matmul(A, x_lstsq).flatten())
print("\n期望的 b 向量 (原始):")
print(b.flatten())

# 检查原始 AX 和 b 之间的残差
original_residuals = np.matmul(A, x_lstsq) - b
print("\n原始 AX 与 b 的残差:")
print(original_residuals.flatten())
print("原始 AX 与 b 的残差平方和:", np.sum(original_residuals**2))

通过这种方法,np.linalg.lstsq 会找到一个 X,它在最小二乘意义上最佳地满足了所有 11 个方程(8个原始方程 + 3个约束方程)。这意味着它会尽可能地使 AX 接近 b,同时精确地满足(或尽可能接近满足,如果系统高度不一致)所有线性约束。

在上述示例中,np.linalg.lstsq 找到的解 x_lstsq 在满足约束的同时,会使 np.matmul(A, x_lstsq) 的结果更接近原始 b 向量,或者在整个增广系统上达到最小的残差。

总结与注意事项

  • 选择方法:

    • 当约束是线性的,并且目标是找到一个既满足原始方程组(或其最佳最小二乘近似)又满足所有约束的解时,增广系统结合 np.linalg.lstsq 是首选。这种方法直接将约束融入到待解系统中,求得的解会同时考虑所有条件。
    • 当约束是非线性的,或者目标函数除了 ||AX - b||^2 之外还有其他复杂的项需要最小化时,scipy.optimize.minimize 及其变体(如 SLSQP)是更通用的选择。但需注意,minimize 仅保证目标函数在约束下的最小值,不一定能使 AX 精确等于 b。
  • 过定系统: 当增广后的方程数多于未知数时(如本例中的 11 个方程,8 个未知数),系统是过定的。np.linalg.lstsq 能够稳健地处理过定系统,找到一个最小二乘意义上的最佳拟合解。

  • rcond 参数: np.linalg.lstsq 中的 rcond 参数用于控制小奇异值的处理,以防止在病态矩阵情况下产生不稳定的解。在较新版本的 NumPy 中,推荐将其设置为 None 以使用默认行为。

通过理解这两种方法的内在机制和适用场景,我们可以更准确、高效地解决带约束的线性方程组问题。对于线性约束,将它们直接融入到方程组中并使用最小二乘求解器,往往能获得更符合预期的结果。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

397

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

575

2023.08.10

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

6

2026.01.30

c++ 字符串格式化
c++ 字符串格式化

本专题整合了c++字符串格式化用法、输出技巧、实践等等内容,阅读专题下面的文章了解更多详细内容。

2

2026.01.30

java 字符串格式化
java 字符串格式化

本专题整合了java如何进行字符串格式化相关教程、使用解析、方法详解等等内容。阅读专题下面的文章了解更多详细教程。

1

2026.01.30

python 字符串格式化
python 字符串格式化

本专题整合了python字符串格式化教程、实践、方法、进阶等等相关内容,阅读专题下面的文章了解更多详细操作。

1

2026.01.30

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

20

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

16

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

18

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号