Линейные уравнения и матрицы. Нелинейные уравнения. SymPy.
В этом уроке вы разберетесь, как в SymPy создавать матрицы, выполнять с ними операции и решать системы линейных уравнений. Затем мы сравним линейные и нелинейные уравнения и посмотрим, как SymPy их решает. В конце есть практика с решениями и чек-лист.
Примечание: Meta Platforms Inc. признана экстремистской, ее продукты запрещены на территории РФ.
Главная мысль: матрицы в SymPy -- это объект для линейной алгебры, который умеет выполнять операции (сложение, умножение, транспонирование, поиск обратной матрицы и определителя). Для уравнений важно выбирать правильную функцию: linsolve для линейных систем, solve для символьного решения, nsolve для численного решения.
Содержание
1. Цели урока
- Понимать базовые термины: матрица, вектор-столбец, размер (shape), линейное и нелинейное уравнение.
- Создавать матрицы разными способами:
Matrix,zeros,ones,eye,diag. - Выполнять операции: сложение, умножение, транспонирование (
T), обратная матрица (inv), определитель (det). - Решать системы линейных уравнений через
linsolveи через матричную форму \(A x = b\). - Решать простые нелинейные уравнения символически (
solve) и численно (nsolve). - Выводить результаты в специальном формате: LaTeX-строка и ASCII pretty-печать.
2. Контекст: матрицы и уравнения в SymPy
Новые термины и определения
SymPy-- библиотека Python для символьной математики: выражения хранятся как объекты, их можно упрощать и преобразовывать.символьное выражение-- объект SymPy, который представляет формулу (например \(x^2 - 2\)), а не конкретное число.матрица-- прямоугольная таблица элементов (чисел или выражений).вектор-столбец-- матрица размера \(n \times 1\).строка матрицы-- горизонтальный набор элементов матрицы.столбец матрицы-- вертикальный набор элементов матрицы.размер (shape)-- пара \((m, n)\), где \(m\) -- число строк, \(n\) -- число столбцов.линейное уравнение-- уравнение, где неизвестные входят только в 1 степени и не перемножаются (пример: \(2x + 3y = 5\)).система уравнений-- несколько уравнений, которые решаются вместе для одних и тех же неизвестных.нелинейное уравнение-- уравнение, где неизвестные входят в степени, корни, знаменатели, тригонометрию и т.д. (пример: \(x^2 - 2 = 0\)).корень уравнения-- значение неизвестной, при котором уравнение становится верным.
import sympy as sp
x = sp.Symbol("x")
expr = x**2 - 2
print(expr)
# x**2 - 2
Частая путаница
x**2 - 2 само по себе не является уравнением. Уравнение появляется, когда вы задаете равенство, например \(x^2 - 2 = 0\), или создаете объект Eq.Eq -- объект SymPy, который хранит уравнение в виде "левая часть равна правой части".import sympy as sp
x = sp.Symbol("x")
eq = sp.Eq(x**2 - 2, 0)
print(eq)
# Eq(x**2 - 2, 0)
3. Создание матриц в SymPy
Matrix из list и list of lists
list (список) -- тип данных Python для хранения последовательности значений. Пример: [1, 2, 3].Конструктор Matrix превращает список в матрицу. Если передать обычный список [1, 2, 3], SymPy создаст вектор-столбец (матрицу \(n \times 1\)).
from sympy import Matrix
col = Matrix([1, 2, 3])
print(col)
# Matrix([[1], [2], [3]])
Если передать двумерный список (list of lists), получится прямоугольная матрица.
from sympy import Matrix
A = Matrix([[1, 2, 3],
[4, 5, 6]])
print(A)
# Matrix([[1, 2, 3], [4, 5, 6]])
итерируемый объект (iterable) -- объект, по которому можно пройти циклом for (например list, tuple, range).from sympy import Matrix
v = Matrix(range(1, 5))
print(v)
# Matrix([[1], [2], [3], [4]])
zeros, ones, eye, diag
конструктор (в контексте урока) -- функция, которая создает объект по заданным параметрам.zeros(n, m)-- матрица \(n \times m\), заполненная нулями.ones(n, m)-- матрица \(n \times m\), заполненная единицами.eye(n)-- единичная матрица \(n \times n\) (единицы на главной диагонали, остальное нули).diag(a, b, c)-- диагональная матрица, где значения стоят на диагонали.
from sympy import zeros, ones, eye, diag
Z = zeros(2, 3)
O = ones(3, 2)
I = eye(3)
D = diag(1, 2, 3)
print(Z)
# Matrix([[0, 0, 0], [0, 0, 0]])
print(O)
# Matrix([[1, 1], [1, 1], [1, 1]])
print(I)
# Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
print(D)
# Matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]])
Типичные ошибки
Ошибка 1: ожидали строку, а получили столбец
Обычный список дает столбец. Чтобы получить строку \(1 \times n\), передайте список списков.
from sympy import Matrix
row = Matrix([[1, 2, 3]])
print(row)
# Matrix([[1, 2, 3]])
Ошибка 2: строки разной длины
Матрица должна быть прямоугольной: все строки одинаковой длины. Ниже показан безопасный способ увидеть ошибку и ее тип.
from sympy import Matrix
try:
bad = Matrix([[1, 2, 3],
[4, 5]]) # строка короче
print(bad)
except Exception as e:
print("ERROR:", type(e).__name__)
# ERROR: ValueError
4. Операции с матрицами
Сложение
покомпонентно (поэлементно) -- "каждый элемент с элементом на той же позиции".Сложение матриц выполняется покомпонентно и возможно только для матриц одинакового размера.
from sympy import Matrix, eye
A = Matrix([[1, 2],
[3, 4]])
B = eye(2)
print(A + B)
# Matrix([[2, 2], [3, 5]])
Умножение: "строка на столбец"
внутренний размер (для умножения \(A B\)) -- число столбцов матрицы \(A\), которое должно совпасть с числом строк матрицы \(B\).Умножение матриц -- это операция "строка на столбец". Она НЕ является поэлементным умножением.
Правило размеров:
$$ (m, n)\cdot(n, p) = (m, p) $$
согласованные размеры -- это когда внутренние размеры совпали (в записи \((m, n)\cdot(n, p)\) совпадает число \(n\)).Как считается один элемент результата:
$$ c_{ij}=\sum_{k=1}^{n} a_{ik} b_{kj} $$
То есть берется \(i\)-я строка матрицы \(A\) и \(j\)-й столбец матрицы \(B\), элементы перемножаются попарно и складываются.
from sympy import Matrix
A = Matrix([[1, 2],
[3, 4]])
B = Matrix([[10, 20],
[30, 40]])
C = A * B
print(C)
# Matrix([[70, 100], [150, 220]])
Мини проверка "вручную" для C[0,0]
Элемент \(c_{11}\) равен \(1\cdot 10 + 2\cdot 30 = 70\).
print(1*10 + 2*30)
# 70
Пример ошибки: размеры не согласованы
Покажем тип ошибки безопасно: через try/except.
from sympy import Matrix
A = Matrix([[1, 2],
[3, 4]]) # shape (2, 2)
B = Matrix([[1],
[2],
[3]]) # shape (3, 1)
try:
print(A * B)
except Exception as e:
print("ERROR:", type(e).__name__)
# ERROR: ShapeError
поэлементное умножение (также встречается термин "произведение Адамара") -- умножение элементов на одинаковых позициях. Оно возможно только для одинаковых размеров.from sympy import Matrix
A = Matrix([[1, 2],
[3, 4]])
B = Matrix([[10, 20],
[30, 40]])
print(A.multiply_elementwise(B))
# Matrix([[10, 40], [90, 160]])
T, inv, det
транспонирование-- операция, которая меняет местами строки и столбцы. В SymPy:M.T.обратная матрица-- матрица \(A^{-1}\), такая что \(A\cdot A^{-1}=I\), где \(I\) -- единичная матрица. В SymPy:A.inv().определитель-- число, которое вычисляется по квадратной матрице. Если \(\det(A)=0\), обратная матрица не существует. В SymPy:A.det().
from sympy import Matrix
M = Matrix([[1, 2, 3],
[4, 5, 6]])
print(M.T)
# Matrix([[1, 4], [2, 5], [3, 6]])
квадратная матрица -- матрица, у которой число строк равно числу столбцов (размер \(n \times n\)).from sympy import diag
A = diag(1, 2, 3, 4)
A_inv = A.inv()
print(A_inv)
# Matrix([[1, 0, 0, 0], [0, 1/2, 0, 0], [0, 0, 1/3, 0], [0, 0, 0, 1/4]])
print(A * A_inv)
# Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
print(A_inv.det())
# 1/24
Типичные ошибки
Ошибка 1: попытка сложить матрицы разных размеров
Сложение требует одинакового shape. Поймаем тип ошибки безопасно.
from sympy import Matrix
A = Matrix([[1, 2],
[3, 4]])
B = Matrix([[1, 2, 3],
[4, 5, 6]])
try:
print(A + B)
except Exception as e:
print("ERROR:", type(e).__name__)
# ERROR: ShapeError
Ошибка 2: попытка взять inv() у матрицы с det == 0
вырожденная матрица -- квадратная матрица с определителем 0. У нее нет обратной матрицы.from sympy import Matrix
S = Matrix([[1, 2],
[2, 4]])
print(S.det())
# 0
try:
print(S.inv())
except Exception as e:
print("ERROR:", type(e).__name__)
# ERROR: NonInvertibleMatrixError
5. Системы линейных уравнений
Что такое линейная система
Систему линейных уравнений удобно записывать в матричном виде:
$$ A x = b $$
матрица коэффициентов A-- матрица чисел перед неизвестными.вектор неизвестных x-- столбец переменных (то, что мы ищем).вектор правых частей b-- столбец чисел справа от знака равенства.коэффициент-- число перед неизвестной (в \(3x\) коэффициент равен 3).
from sympy import Matrix, symbols
x, y = symbols("x y")
A = Matrix([[2, 1],
[5, 3]])
vec = Matrix([x, y])
b = Matrix([1, 2])
print(A)
# Matrix([[2, 1], [5, 3]])
print(vec)
# Matrix([[x], [y]])
print(b)
# Matrix([[1], [2]])
Решение через linsolve
linsolve -- функция SymPy, которая решает системы линейных уравнений и возвращает множество решений.кортеж (tuple) -- неизменяемая последовательность в Python, записывается как (a, b, c).Пример системы с единственным решением:
$$ \begin{cases} x + 2y + 3z = 3 \\ 4x + 5y + 6z = 6 \\ 7x + 8y + 10z = 9 \end{cases} $$
from sympy import Matrix, linsolve, symbols
x, y, z = symbols("x y z")
A = Matrix([[1, 2, 3],
[4, 5, 6],
[7, 8, 10]])
b = Matrix([3, 6, 9])
sol = linsolve((A, b), (x, y, z))
print(sol)
# {(-1, 2, 0)}
бесконечно много решений-- когда можно выбирать одну или несколько переменных свободно, и все равно получаются решения.параметр-- специальный символ (tau0,tau1и т.д.), который означает "можно взять любое значение".параметрическое решение-- решение, записанное через параметры.
Пример зависимой системы (бесконечно много решений):
$$ \begin{cases} x + y + z = 3 \\ 2x + 2y + 2z = 6 \end{cases} $$
from sympy import linsolve, symbols
x, y, z = symbols("x y z")
sol = linsolve([x + y + z - 3,
2*x + 2*y + 2*z - 6], (x, y, z))
print(sol)
# {(3 - tau0 - tau1, tau0, tau1)}
Матричная форма и LUsolve
LUsolve -- метод матрицы SymPy, который решает систему \(A x = b\) с помощью LU-разложения.LU-разложение-- представление матрицы \(A\) как произведения \(L\cdot U\).нижнетреугольная матрица L-- матрица, где элементы выше главной диагонали равны нулю.верхнетреугольная матрица U-- матрица, где элементы ниже главной диагонали равны нулю.
from sympy import Matrix
A = Matrix([[2, 1],
[5, 3]])
b = Matrix([1, 2])
x = A.LUsolve(b)
print(x)
# Matrix([[1], [-1]])
Типичные ошибки
Ошибка 1: использовать linsolve для нелинейной задачи
linsolve предназначен для линейных систем. Если в уравнении есть \(x^2\), \(\sin(x)\), \(1/x\) и т.д., это уже нелинейность. Ниже мы не "угадываем" текст ошибки, а надежно выводим тип исключения.
from sympy import linsolve, symbols
x = symbols("x")
try:
sol = linsolve([x**2 - 1], (x,))
print(sol)
except Exception as e:
print("ERROR:", type(e).__name__)
# ERROR: NonlinearError
Ошибка 2: не передать список неизвестных
Тогда SymPy может вернуть ответ через параметр tau0. Это не "плохо", но студенту сложнее читать ответ без объяснения.
from sympy import Matrix, linsolve
A = Matrix([[1, 1],
[2, 2]])
b = Matrix([3, 6])
print(linsolve((A, b)))
# {(tau0, 3 - tau0)}
6. Нелинейные уравнения и формат вывода
Что такое нелинейное уравнение
Нелинейное уравнение -- это уравнение, где неизвестная входит не линейно: в квадрате, в степени, в знаменателе, под корнем, внутри функций (\(\sin\), \(\cos\), \(\exp\) и т.д.).
Примеры:
$$ x^2 - 2 = 0 $$
$$ \frac{1}{x} - 3 = 0 $$
import sympy as sp
x = sp.Symbol("x")
print(sp.Eq(x**2 - 2, 0))
# Eq(x**2 - 2, 0)
print(sp.Eq(1/x - 3, 0))
# Eq(1/x - 3, 0)
solve и nsolve
solve-- функция SymPy, которая пытается найти символьное (точное) решение.nsolve-- функция SymPy для численного решения (приближенный корень).начальная догадка-- число, с которого численный метод начинает поиск корня.сходимость-- ситуация, когда численный метод приближается к решению и заканчивает работу успешно.
Символьное решение (точно) для \(x^2 - 2 = 0\):
from sympy import symbols, solve
x = symbols("x")
sol = solve(x**2 - 2, x)
print(sol)
# [-sqrt(2), sqrt(2)]
Численное решение (приближенно) для \(x^2 - 2 = 0\):
from sympy import Symbol, nsolve
x = Symbol("x")
root = nsolve(x**2 - 2, 1) # начальная догадка 1
print(root)
# 1.41421356237310
Один запуск nsolve обычно дает один корень
Чтобы найти другой корень, меняют начальную догадку.
from sympy import Symbol, nsolve
x = Symbol("x")
r1 = nsolve(x**2 - 2, 1)
r2 = nsolve(x**2 - 2, -1)
print(r1)
# 1.41421356237310
print(r2)
# -1.41421356237310
Специальный вывод: LaTeX и ASCII pretty
формат вывода-- способ превратить объект SymPy в текст, который можно показать пользователю.LaTeX-- текстовый язык для записи математических формул.pretty-печать-- вывод в виде многострочной "псевдографики", чтобы дроби и степени выглядели наглядно.ASCII-- базовый набор символов (латиница, цифры, знаки). Обычно безопасен для вставки в CMS.
1) LaTeX-строка для выражения \(\frac{(x+1)^2}{2}\) через latex().
from sympy import symbols
from sympy.printing.latex import latex
x = symbols("x")
expr = (x + 1)**2 / 2
print(latex(expr))
# \frac{\left(x + 1\right)^{2}}{2}
LaTeX-строка -- обычная строка Python, внутри которой находятся команды LaTeX (например \frac, \sqrt).2) ASCII pretty-печать (без Unicode) через pretty(..., use_unicode=False).
from sympy import symbols
from sympy.printing.pretty import pretty
x = symbols("x")
expr = (x + 1)**2 / 2
print(pretty(expr, use_unicode=False))
# 2
# (x + 1)
# --------
# 2
Типичные ошибки
Ошибка 1: ожидать, что nsolve вернет все корни
nsolve обычно ищет один корень рядом с начальной догадкой. Это нормальное поведение.
from sympy import Symbol, nsolve
x = Symbol("x")
print(nsolve(x**2 - 2, 1))
# 1.41421356237310
Ошибка 2: использовать Unicode pretty-печать и копировать вывод в CMS
Если у вас бывают проблемы с кодировками, используйте ASCII: use_unicode=False.
from sympy import symbols
from sympy.printing.pretty import pretty
x = symbols("x")
expr = 1 / (x + 1)
print(pretty(expr, use_unicode=False))
# 1
# -----
# x + 1
7. Практика: задачи (с решениями)
Блок 1: матрицы
Задача 1: создать матрицу 3 x 3
Создайте матрицу \(3 \times 3\) через Matrix и выведите ее в консоль.
from sympy import Matrix
A = Matrix([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(A)
# Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
Задача 2: прибавить единичную матрицу
Прибавьте \(I\) (единичную матрицу) к матрице \(A\) из задачи 1.
from sympy import Matrix, eye
A = Matrix([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(A + eye(3))
# Matrix([[2, 2, 3], [4, 6, 6], [7, 8, 10]])
Задача 3: умножить матрицы (строка на столбец)
Перемножьте матрицы и выведите результат.
from sympy import Matrix
A = Matrix([[1, 2],
[3, 4]])
B = Matrix([[10, 20],
[30, 40]])
print(A * B)
# Matrix([[70, 100], [150, 220]])
Задача 4: найти определитель и сделать вывод об обратимости
Если \(\det(A)\neq 0\), матрица обратима (у нее есть \(A^{-1}\)).
from sympy import Matrix
A = Matrix([[2, 1],
[5, 3]])
d = A.det()
print(d)
# 1
print(d != 0)
# True
Блок 2: уравнения и вывод
Задача 5: решить систему линейных уравнений через linsolve
Решите систему:
$$ \begin{cases} 2x + y = 1 \\ 5x + 3y = 2 \end{cases} $$
from sympy import Matrix, linsolve, symbols
x, y = symbols("x y")
A = Matrix([[2, 1],
[5, 3]])
b = Matrix([1, 2])
sol = linsolve((A, b), (x, y))
print(sol)
# {(1, -1)}
Задача 6: решить нелинейное уравнение через solve
Найдите корни уравнения:
$$ x^2 - 2 = 0 $$
from sympy import symbols, solve
x = symbols("x")
print(solve(x**2 - 2, x))
# [-sqrt(2), sqrt(2)]
Задача 7: получить ASCII pretty-печать
Выведите выражение \(\frac{(x+1)^2}{2}\) в виде многострочной ASCII-формулы.
from sympy import symbols
from sympy.printing.pretty import pretty
x = symbols("x")
expr = (x + 1)**2 / 2
print(pretty(expr, use_unicode=False))
# 2
# (x + 1)
# --------
# 2
Задача 8: вывести выражение в LaTeX
Сформируйте LaTeX-строку для \(\frac{(x+1)^2}{2}\).
from sympy import symbols
from sympy.printing.latex import latex
x = symbols("x")
expr = (x + 1)**2 / 2
print(latex(expr))
# \frac{\left(x + 1\right)^{2}}{2}
8. Чек-лист самопроверки
Отметьте пункты, которые вы действительно понимаете и можете повторить без подсказок.
| +/- | Навык | Проверка |
|---|---|---|
| Понимаю, что такое матрица и ее shape | Могу объяснить: строки, столбцы, размер \((m, n)\) | |
| Создаю матрицу через Matrix | Могу получить столбец из Matrix([1,2,3]) и матрицу из Matrix([[...],[...]]) |
|
| Использую zeros/ones/eye/diag | Могу создать матрицы \(n \times m\) и \(n \times n\) через конструкторы | |
| Складываю матрицы правильно | Понимаю, что нужны одинаковые размеры (shape совпадает) | |
| Умножаю матрицы правильно | Могу проверить внутренний размер и объяснить формулу \(c_{ij}=\sum a_{ik}b_{kj}\) | |
| Отличаю матричное умножение от поэлементного | Знаю разницу между A*B и A.multiply_elementwise(B) |
|
| Делаю транспонирование | Могу получить M.T и объяснить, что строки стали столбцами |
|
| Понимаю det и обратимость | Могу объяснить: если \(\det(A)=0\), то \(A^{-1}\) не существует | |
| Решаю линейные системы | Могу решить через linsolve и прочитать ответ как кортеж |
|
| Решаю нелинейные уравнения | Понимаю разницу между solve (символьно) и nsolve (численно) |
|
| Делаю специальный вывод | Могу получить LaTeX через latex() и ASCII pretty через pretty(..., use_unicode=False) |