Способы решения систем уравнений в 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]

Решение систем уравнений в Python - comments

En
Python решение системы (python)