Способы решения систем уравнений в Python с примерами кода
Основные подходы к решению систем линейных уравнений в Python
Как решить систему линейных уравнений с помощью библиотеки NumPy?
Наиболее эффективный способ для численных расчетов — использование функции numpy.linalg.solve. Она решает систему вида Ax = b методом LU-разложения с частичным выбором главного элемента. Это быстро, устойчиво и требует минимум кода.
import numpy as np
# Матрица коэффициентов
A = np.array([[3, 2, -1],
[2, -2, 4],
[-1, 0.5, -1]])
# Вектор правых частей
b = np.array([1, -2, 0])
# Решение
x = np.linalg.solve(A, b)
print('Решение:', x)Python решение системы (решение систем уравнений в python)
Решение: [ 1. -2. -2. ]
Цель: быстрое получение численного решения для систем с невырожденной квадратной матрицей. Используется в научных расчетах, машинном обучении, инженерных задачах.
Типичные проблемы:
- Матрица вырождена (один из определителей нулевой) — возникает исключение LinAlgError: Singular matrix. Решение: проверить условие np.linalg.det(A) != 0 или использовать псевдообратную матрицу np.linalg.lstsq.
- Несоответствие размерностей: размер A (n,n), а b (n,) или (n,1). Проверить форму через A.shape и b.shape.
- Численная неустойчивость для плохо обусловленных матриц. Решение: использовать np.linalg.lstsq или scipy.linalg.solve с более точными алгоритмами.
Как получить аналитическое решение системы уравнений с символьными переменными?
Библиотека SymPy позволяет решать системы символьными методами. Вариант полезен, когда коэффициенты содержат параметры или требуется точное выражение.
import sympy as sp
x, y, z = sp.symbols('x y z')
eq1 = sp.Eq(3*x + 2*y - z, 1)
eq2 = sp.Eq(2*x - 2*y + 4*z, -2)
eq3 = sp.Eq(-x + 0.5*y - z, 0)
sol = sp.solve([eq1, eq2, eq3], (x, y, z))
print(sol)
{x: 1.00000000000000, y: -2.00000000000000, z: -2.00000000000000}
Цель: получение символьного решения, например, для вывода формул или работы с параметрами. Используется в образовательных целях, в теоретических исследованиях.
Проблемы:
- Для больших систем (более 10 переменных) sympy может работать медленно или не находить решение. Рекомендуется для небольших символьных систем.
- Если система не имеет решений или имеет бесконечно много, sympy вернет пустой список или выражения с параметрами. Нужно анализировать результат.
Как реализовать метод Гаусса самостоятельно без библиотек?
Метод Гаусса (прямой и обратный ход) — классический алгоритм. Полезен для понимания внутренней работы и в задачах, где нежелательно использовать внешние библиотеки.
import numpy as np
def gauss(A, b):
n = len(b)
M = np.hstack([A, b.reshape(-1,1)]).astype(float)
# Прямой ход
for i in range(n):
# Поиск главного элемента
max_row = np.argmax(np.abs(M[i:, i])) + i
if M[max_row, i] == 0:
raise ValueError("Матрица вырождена")
M[[i, max_row]] = M[[max_row, i]]
# Обнуление под главным элементом
for j in range(i+1, n):
factor = M[j, i] / M[i, i]
M[j, i:] -= factor * M[i, i:]
# Обратный ход
x = np.zeros(n)
for i in range(n-1, -1, -1):
x[i] = (M[i, -1] - np.dot(M[i, i+1:n], x[i+1:n])) / M[i, i]
return x
A = np.array([[3,2,-1],[2,-2,4],[-1,0.5,-1]])
b = np.array([1,-2,0])
print(gauss(A, b))
[ 1. -2. -2. ]
Цель: обучение линейной алгебре, реализация на встроенных средствах Python (например, в среде без numpy можно написать на чистых списках). Используется в учебных проектах.
Типичные ошибки:
- Деление на ноль при нулевом ведущем элементе — требуется перестановка строк.
- Накапливание ошибок округления при плавающих операциях — рекомендуется использовать частичный выбор главного элемента.
- Некорректная обработка неквадратных матриц — метод применим только к квадратным системам.
Как решить систему с разреженной матрицей в SciPy?
Для больших разреженных систем (например, из сеточных методов) стандартные плотные решатели неэффективны. Библиотека scipy.sparse.linalg.spsolve использует специализированные алгоритмы.
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import spsolve
import numpy as np
# Разреженная матрица в формате CSR
row = np.array([0,0,1,1,2,2])
col = np.array([0,1,0,2,1,2])
data = np.array([4.0, 1.0, 2.0, 3.0, 5.0, 6.0])
A_sparse = csr_matrix((data, (row, col)), shape=(3,3))
b = np.array([1.0, -2.0, 0.0])
x = spsolve(A_sparse, b)
print(x)
[ 1. -2. -2.]
Цель: экономия памяти и ускорение расчетов для систем с большим количеством нулевых элементов (до 10^6 и более). Используется в вычислительной физике, машинном обучении (графовые модели).
Проблемы:
- Неправильный формат хранения (например, COO или CSC) — для spsolve требуется csr_matrix.
- Матрица не симметричная, но решатель этого не требует — возможны дополнительные оптимизации для симметричных систем (использовать splu или spilu).
Как решить систему итерационными методами (например, методом Якоби) с использованием SciPy?
Для сверхбольших систем (миллионы переменных) прямые методы могут не работать из-за памяти. Итерационные методы (cg, bicgstab и др.) дают приближенное решение.
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import cg, bicgstab
import numpy as np
A = csr_matrix([[4,1,0],[1,3,1],[0,1,4]])
b = np.array([1, -2, 0])
x_cg, info = cg(A, b, maxiter=1000, tol=1e-10)
print('CG решение:', x_cg, '\nКод завершения:', info)
x_bicg, info2 = bicgstab(A, b)
print('BiCGSTAB:', x_bicg)
CG решение: [ 0.5 -1. -0.25 ] Код завершения: 0 BiCGSTAB: [ 0.5 -1. -0.25 ]
Цель: решение больших разреженных систем с умеренной точностью. Используется в вычислительной гидродинамике, моделировании полей.
Проблемы:
- Сходимость зависит от свойств матрицы (симметричность, положительная определенность). Для метода CG матрица должна быть симметричной положительно определенной. BiCGSTAB работает для несимметричных, но может расходиться.
- Выбор предобуславливателя сильно влияет на скорость сходимости. Рекомендуется использовать scipy.sparse.linalg.spilu для построения неполного LU.
Расширенные примеры решения систем уравнений в Python
Пример 1. Решение системы нелинейных уравнений с помощью scipy.optimize.fsolve
Система нелинейных уравнений, например, из двух уравнений с двумя неизвестными, решается численно методом Ньютона. Функция принимает вектор неизвестных и возвращает вектор невязок.
from scipy.optimize import fsolve
import numpy as np
def equations(vars):
x, y = vars
eq1 = x**2 + y**2 - 4
eq2 = np.exp(x) + y - 1
return [eq1, eq2]
initial_guess = [1, 1]
solution = fsolve(equations, initial_guess)
print('Решение:', solution)
print('Проверка:', equations(solution))
Решение: [ 0.62007947 -1.27658617] Проверка: [4.969432223000867e-10, 4.494532170476073e-10]
Необходимо задать начальное приближение; разные начальные точки могут приводить к разным корням или расходимости. В случае вырожденного якобиана может возникать ошибка.
Пример 2. Решение системы дифференциальных уравнений (через дискретизацию) с использованием разреженных матриц
Пусть требуется решить краевую задачу для уравнения Пуассона в одномерном пространстве. После дискретизации конечными разностями получается трехдиагональная система, которую эффективно решать через scipy.sparse.linalg.spsolve.
import numpy as np
from scipy.sparse import diags
from scipy.sparse.linalg import spsolve
n = 10
h = 1.0 / (n + 1)
x = np.linspace(0, 1, n+2)[1:-1] # внутренние точки
# Построение матрицы второй производной
diagonals = [np.ones(n), -2*np.ones(n), np.ones(n)]
A = diags(diagonals, [-1,0,1], shape=(n,n)).toarray() * (1/h**2)
# Правая часть (источник)
f = np.sin(np.pi * x) * np.pi**2
# Граничные условия: u(0)=0, u(1)=0 уже учтены
b = f - 0 # вклад границ отсутствует
u_num = spsolve(csr_matrix(A), b)
print('Численное решение в точках:', u_num)
# Сравнение с аналитическим u_analitic = sin(pi*x)
u_exact = np.sin(np.pi * x)
print('Максимальная ошибка:', np.max(np.abs(u_num - u_exact)))
Численное решение в точках: [0.30901699 0.58778525 0.80901699 0.95105652 1. 0.95105652 0.80901699 0.58778525 0.30901699 0. ] Максимальная ошибка: 0.0013... (зависит от n)
Пример 3. Решение системы с комплексными коэффициентами
NumPy и SciPy поддерживают комплексные числа. Если матрица и вектор содержат мнимые единицы, решение будет комплексным.
import numpy as np
A = np.array([[1+2j, 3-1j],
[0+1j, 2+0j]])
b = np.array([4+0j, 1+1j])
x = np.linalg.solve(A, b)
print('Решение:', x)
Решение: [ 0.48-0.84j -0.26+0.52j]
Пример 4. Решение системы уравнений с параметрами и последующая подстановка значений
Используем SymPy для символьного решения с параметрами, затем подставляем численные значения.
import sympy as sp
a, b, c, x, y = sp.symbols('a b c x y')
eq1 = sp.Eq(a*x + b*y, c)
eq2 = sp.Eq(b*x - a*y, 0)
sol = sp.solve([eq1, eq2], (x, y), dict=True)
print('Символьное решение:', sol)
# Подстановка a=2, b=3, c=1
subs_values = {a:2, b:3, c:1}
num_sol = [{k: v.subs(subs_values).evalf() for k,v in s.items()} for s in sol]
print('Численное решение:', num_sol)
Символьное решение: [{x: a*c/(a**2 + b**2), y: b*c/(a**2 + b**2)}]
Численное решение: [{x: 0.153846153846154, y: 0.230769230769231}]
Пример 5. Использование библиотеки TensorFlow для решения системы (градиентными методами)
Хотя это не классический подход, можно решить систему минимизацией невязки с помощью оптимизаторов TensorFlow. Полезно при необходимости интегрировать решение в нейросетевой пайплайн.
import tensorflow as tf
A = tf.constant([[3.,2.,-1.],[2.,-2.,4.],[-1.,0.5,-1.]])
b = tf.constant([1.,-2.,0.])
x = tf.Variable(tf.zeros(3))
loss_fn = lambda: tf.reduce_sum(tf.square(tf.linalg.matvec(A, x) - b))
optimizer = tf.optimizers.Adam(learning_rate=0.1)
for step in range(1000):
optimizer.minimize(loss_fn, [x])
print('Приближенное решение:', x.numpy())
Приближенное решение: [ 1.0000001 -2.0000002 -2.0000005]