0Pricing
Pandas & NumPy Academy · 课时

线性方程组求解

使用 np.linalg.solve 求解 Ax=b,并与简单的逆矩阵乘法方法进行比较,以评估数值稳定性

线性方程组求解 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

What Is a Linear System?

A system of linear equations can be written in matrix form as Ax = b, where A is the coefficient matrix, x is the vector of unknowns, and b is the right-hand side vector. For example, two equations with two unknowns can be expressed as a 2×2 matrix equation. Solving the system means finding x such that A multiplied by x equals b. NumPy's np.linalg.solve(A, b) handles this efficiently and accurately.

import numpy as np

# System: 2x + y = 5
#         x + 3y = 10
A = np.array([[2.0, 1.0],
              [1.0, 3.0]])
b = np.array([5.0, 10.0])

x = np.linalg.solve(A, b)
print('Solution x:', x)
print('Verify A @ x == b:', np.allclose(A @ x, b))

Why Not Use the Inverse?

Mathematically, the solution to Ax = b is x = A⁻¹b. But computing np.linalg.inv(A) @ b is slower and less accurate than calling np.linalg.solve(A, b) directly. solve() uses LU decomposition internally, which requires fewer operations and accumulates less floating-point error. The rule of thumb: if you are solving for x in Ax=b, always use solve, never inv.

import numpy as np

A = np.random.rand(100, 100)
b = np.random.rand(100)

# Avoid this pattern
x_inv = np.linalg.inv(A) @ b

# Prefer this
x_solve = np.linalg.solve(A, b)

print('Max difference:', np.max(np.abs(x_inv - x_solve)))
# solve is faster AND more accurate

Requirements for solve()

np.linalg.solve(A, b) requires that A is square and non-singular. If A is singular (determinant is zero), the function raises np.linalg.LinAlgError: Singular matrix. A is singular when rows are linearly dependent — for example, one equation is just a multiple of another. Always check that your system is well-posed before calling solve. If A is not square, use np.linalg.lstsq() instead.

import numpy as np

# Well-posed system
A = np.array([[1.0, 2.0], [3.0, 4.0]])
b = np.array([5.0, 6.0])
print('Solution:', np.linalg.solve(A, b))

# Singular system (row 2 = 3 * row 1)
A_singular = np.array([[1.0, 2.0], [3.0, 6.0]])
try:
    np.linalg.solve(A_singular, b)
except np.linalg.LinAlgError as e:
    print('Error:', e)

LU Decomposition Under the Hood

np.linalg.solve internally applies LU decomposition: it factors A into a lower-triangular matrix L and an upper-triangular matrix U such that A = LU. Solving then becomes two simpler triangular solves (forward and back substitution), which are far more efficient than Gaussian elimination repeated from scratch. This is the same algorithm used by MATLAB, Fortran LAPACK, and virtually every scientific computing library.

import numpy as np
from scipy import linalg

A = np.array([[2.0, 1.0, -1.0],
              [-3.0, -1.0, 2.0],
              [-2.0, 1.0, 2.0]])
b = np.array([8.0, -11.0, -3.0])

# NumPy solve (uses LAPACK dgesv under the hood)
x = np.linalg.solve(A, b)
print('Solution:', x)
print('Check:', np.allclose(A @ x, b))

Solving Multiple Right-Hand Sides

np.linalg.solve(A, B) can handle a matrix B with multiple columns, solving for each column simultaneously. If B has shape (n, k), the result X has shape (n, k), where each column of X is the solution for the corresponding column of B. This is useful in physics simulations and machine learning where you need to solve the same coefficient matrix with many different right-hand sides.

import numpy as np

A = np.array([[3.0, 1.0],
              [1.0, 2.0]])

# Three right-hand side vectors at once
B = np.array([[9.0, 3.0, 6.0],
              [8.0, 4.0, 2.0]])

X = np.linalg.solve(A, B)
print('Solutions X shape:', X.shape)  # (2, 3)
print('Verify A @ X == B:', np.allclose(A @ X, B))

Least Squares with np.linalg.lstsq()

When the system is over-determined (more equations than unknowns), no exact solution exists. np.linalg.lstsq(A, b) finds the x that minimises the sum of squared residuals ||Ax - b||² — this is exactly what linear regression computes. It returns the solution x, residuals, rank, and singular values. The rcond=None argument suppresses a deprecation warning and sets the cutoff for treating singular values as zero.

import numpy as np

# Over-determined: 4 equations, 2 unknowns
A = np.array([[1.0, 1.0],
              [1.0, 2.0],
              [1.0, 3.0],
              [1.0, 4.0]])
b = np.array([2.0, 2.5, 3.5, 4.5])

x, residuals, rank, sv = np.linalg.lstsq(A, b, rcond=None)
print('Least-squares solution:', x)
print('Residuals:', residuals)
print('Rank:', rank)

Applying solve() in Linear Regression

The normal equations of ordinary least squares are A.T @ A @ x = A.T @ b, which is a square system solvable with np.linalg.solve. This gives the same result as lstsq when A.T @ A is well-conditioned. In practice, lstsq is preferred because it uses SVD and is stable even when columns are nearly collinear, but the normal equations help you understand the math behind regression.

import numpy as np

np.random.seed(0)
X = np.column_stack([np.ones(50), np.random.rand(50) * 10])
y = 3.0 + 2.0 * X[:, 1] + np.random.randn(50)

# Solve normal equations
coeffs = np.linalg.solve(X.T @ X, X.T @ y)
print('Intercept:', round(coeffs[0], 3))
print('Slope:', round(coeffs[1], 3))

Checking the Condition Number

Before solving, it is good practice to check the condition number with np.linalg.cond(A). A condition number close to 1 means the system is well-conditioned and the solution is reliable. A very large condition number (e.g., 1e12) means the matrix is nearly singular: small changes in b produce huge changes in x, and the solution may be numerically unreliable. In that case, consider regularisation (ridge regression) or using a more robust solver.

import numpy as np

# Well-conditioned
A1 = np.array([[2.0, 1.0], [1.0, 3.0]])
print('Condition number (well):', np.linalg.cond(A1))

# Ill-conditioned (nearly linearly dependent rows)
A2 = np.array([[1.0, 1.0], [1.0, 1.0001]])
print('Condition number (ill):', np.linalg.cond(A2))

Sparse Systems: When Not to Use linalg

For very large systems where A has mostly zero entries (a sparse matrix), storing the full matrix wastes memory and using dense np.linalg.solve is slow. SciPy provides scipy.sparse and scipy.sparse.linalg.spsolve that exploit sparsity for huge systems like finite element models or network problems. Understanding when your matrix is sparse is an important performance consideration in scientific computing.

import numpy as np

# Dense solve is fine for small-to-medium systems
A = np.random.rand(200, 200)
b = np.random.rand(200)
x = np.linalg.solve(A, b)
print('Dense solve OK, solution shape:', x.shape)

# For large sparse systems, use scipy.sparse.linalg.spsolve instead
# from scipy.sparse.linalg import spsolve
# x = spsolve(A_sparse, b)

Interpreting the Solution Vector

Once you have the solution vector x from np.linalg.solve(A, b), always verify it by computing A @ x and checking it against b using np.allclose(A @ x, b). In floating-point arithmetic, you rarely get exact equality — allclose checks within a small tolerance. The residual ||Ax - b|| should be near machine epsilon times the scale of the problem for a well-conditioned system.

import numpy as np

A = np.array([[4.0, 3.0, 2.0],
              [1.0, 5.0, 3.0],
              [2.0, 1.0, 6.0]])
b = np.array([10.0, 20.0, 15.0])

x = np.linalg.solve(A, b)
print('Solution:', np.round(x, 4))

# Residual check
residual = np.linalg.norm(A @ x - b)
print('Residual ||Ax - b||:', residual)
print('Valid:', np.allclose(A @ x, b))

Solving in Machine Learning Pipelines

Linear solvers appear throughout machine learning: linear regression solves the normal equations, Gaussian processes solve kernel matrix systems, and Kalman filters solve state-update equations. By understanding np.linalg.solve and lstsq, you can implement these algorithms from scratch and debug them when results are unexpected. NumPy's linalg module gives you the same numerical tools used in production scientific libraries.

import numpy as np

# Ridge regression: (X.T @ X + lambda * I) @ w = X.T @ y
np.random.seed(42)
X = np.random.rand(100, 5)
y = np.random.rand(100)
lambda_reg = 0.1
n_features = X.shape[1]

A = X.T @ X + lambda_reg * np.eye(n_features)
bvec = X.T @ y
w = np.linalg.solve(A, bvec)
print('Ridge regression weights:', np.round(w, 4))

Quick Check

Test your understanding of Data Analysis concepts from this lesson.

Lesson Recap

In this lesson you learned: np.linalg.solve(A, b) efficiently solves square linear systems using LU decomposition, np.linalg.lstsq() finds the least-squares solution for over-determined systems, and the condition number measures numerical stability — large values warn of near-singular matrices. Next up we explore eigenvalues and the SVD, which underpin PCA and many ML algorithms.

常见问题解答

「线性方程组求解」课时是免费的吗?

是的 — 「线性方程组求解」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。

「线性方程组求解」这节课中我会学到什么?

使用 np.linalg.solve 求解 Ax=b,并与简单的逆矩阵乘法方法进行比较,以评估数值稳定性 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Pandas & NumPy Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「线性方程组求解」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?

能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 np.matmul 和 @ 进行矩阵乘法
  2. 行列式、逆矩阵与转置
  3. 线性方程组求解
  4. 特征值与 SVD 概览
← 返回 Pandas & NumPy Academy