Решение систем линейных уравнений
Решите уравнение Ax=b с помощью np.linalg.solve и сравните этот способ с наивным умножением на обратную матрицу с точки зрения численной устойчивости
«Решение систем линейных уравнений» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Что такое линейная система?
Систему линейных уравнений можно записать в матричной форме как Ax = b, где A — матрица коэффициентов, x — вектор неизвестных, а b — вектор правой части. Например, два уравнения с двумя неизвестными можно представить в виде матричного уравнения с матрицей 2×2. Решение системы означает поиск такого x, что произведение A на x равно b. np.linalg.solve(A, b) из NumPy решает эту задачу эффективно и точно.
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))Почему не следует использовать обратную матрицу?
Математически решение уравнения Ax = b имеет вид x = A⁻¹b. Но вычисление np.linalg.inv(A) @ b медленнее и менее точно, чем непосредственный вызов np.linalg.solve(A, b). solve() использует внутри LU-разложение, которому требуется меньше операций и при котором накапливается меньшая погрешность вычислений с плавающей точкой. Практическое правило: если вы решаете уравнение относительно x в системе Ax=b, всегда используйте solve, а не 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Требования к solve()
np.linalg.solve(A, b) требует, чтобы A была квадратной и невырожденной. Если A вырождена (определитель равен нулю), функция возбуждает исключение np.linalg.LinAlgError: Singular matrix. Матрица A является вырожденной, когда её строки линейно зависимы — например, если одно уравнение является просто умноженным на число другим уравнением. Всегда проверяйте корректность постановки системы перед вызовом solve. Если A не квадратная, вместо этого используйте np.linalg.lstsq().
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-разложение внутри алгоритма
np.linalg.solve внутри применяет LU-разложение: раскладывает A на нижнетреугольную матрицу L и верхнетреугольную матрицу U так, что A = LU. Затем решение сводится к двум более простым решениям треугольных систем (прямой и обратной подстановке), что гораздо эффективнее, чем многократное выполнение исключения Гаусса с нуля. Этот же алгоритм используется в MATLAB, Fortran LAPACK и практически всех библиотеках научных вычислений.
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))Решение систем с несколькими правыми частями
np.linalg.solve(A, B) может работать с матрицей B с несколькими столбцами, решая систему одновременно для каждого столбца. Если B имеет форму (n, k), результат X имеет форму (n, k), причём каждый столбец X является решением для соответствующего столбца B. Это полезно в физических моделях и машинном обучении, когда одну и ту же матрицу коэффициентов нужно использовать для множества разных правых частей.
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))Метод наименьших квадратов с np.linalg.lstsq()
Когда система переопределена (уравнений больше, чем неизвестных), точного решения не существует. np.linalg.lstsq(A, b) находит такое x, которое минимизирует сумму квадратов невязок ||Ax - b||² — именно это и вычисляет линейная регрессия. Функция возвращает решение x, невязки, ранг и сингулярные значения. Аргумент rcond=None подавляет предупреждение об устаревании и задаёт порог, ниже которого сингулярные значения считаются нулевыми.
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)Применение solve() в линейной регрессии
Нормальные уравнения обычного метода наименьших квадратов имеют вид A.T @ A @ x = A.T @ b — это квадратная система, которую можно решить с помощью np.linalg.solve. При хорошо обусловленной матрице A.T @ A результат совпадает с результатом lstsq. На практике предпочтительнее lstsq, поскольку эта функция использует SVD и остаётся устойчивой даже при почти линейно зависимых столбцах, но нормальные уравнения помогают понять математическую основу регрессии.
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))Проверка числа обусловленности
Перед решением системы полезно проверить число обусловленности с помощью np.linalg.cond(A). Число обусловленности, близкое к 1, означает, что система хорошо обусловлена, а её решение надёжно. Очень большое число обусловленности (например, 1e12) означает, что матрица почти вырождена: небольшие изменения b вызывают огромные изменения x, и решение может быть численно ненадёжным. В таком случае рассмотрите регуляризацию (гребневую регрессию) или использование более устойчивого решателя.
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))Разреженные системы: когда не следует использовать linalg
Для очень больших систем, в которых A содержит преимущественно нулевые элементы (разреженная матрица), хранение полной матрицы расходует память, а использование плотной функции np.linalg.solve работает медленно. SciPy предоставляет scipy.sparse и scipy.sparse.linalg.spsolve, которые используют разреженность для решения огромных систем, например в моделях метода конечных элементов или сетевых задачах. Умение определять, является ли матрица разреженной, важно для производительности научных вычислений.
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)Интерпретация вектора решения
Получив вектор решения x с помощью np.linalg.solve(A, b), всегда проверяйте его, вычисляя A @ x и сравнивая результат с b с помощью np.allclose(A @ x, b). При вычислениях с плавающей точкой точное равенство встречается редко — allclose проверяет равенство с небольшой допустимой погрешностью. Невязка ||Ax - b|| для хорошо обусловленной системы должна быть близка к произведению машинного эпсилона и масштаба задачи.
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))Решение систем в конвейерах машинного обучения
Линейные решатели используются во многих областях машинного обучения: линейная регрессия решает нормальные уравнения, гауссовские процессы решают системы с матрицами ядер, а фильтры Калмана решают уравнения обновления состояния. Понимая работу np.linalg.solve и lstsq, вы сможете реализовать эти алгоритмы с нуля и находить в них ошибки, когда результаты оказываются неожиданными. Модуль linalg в NumPy предоставляет те же численные инструменты, которые используются в промышленных библиотеках научных вычислений.
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))Быстрая проверка
Проверьте своё понимание концепций анализа данных из этого урока.
Итоги урока
В этом уроке вы узнали, что np.linalg.solve(A, b) эффективно решает квадратные линейные системы с помощью LU-разложения; np.linalg.lstsq() находит решение методом наименьших квадратов для переопределённых систем; а число обусловленности измеряет численную устойчивость — большие значения предупреждают о почти вырожденных матрицах. Далее мы рассмотрим собственные значения и SVD, лежащие в основе PCA и многих алгоритмов машинного обучения.
Изучай Python с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 30
- Уроки
- 120
Часто задаваемые вопросы
Урок «Решение систем линейных уравнений» бесплатный?
Да — полный текст урока «Решение систем линейных уравнений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Чему я научусь в уроке «Решение систем линейных уравнений»?
Решите уравнение Ax=b с помощью np.linalg.solve и сравните этот способ с наивным умножением на обратную матрицу с точки зрения численной устойчивости Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?
Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Решение систем линейных уравнений»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?
Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Умножение матриц с помощью np.matmul и @
- Определители, обратные матрицы и транспонирование
- Решение систем линейных уравнений
- Собственные значения и обзор SVD