Что такое SymPy: точное против приближённого

До сих пор мы считали числа приближённо. SymPy умеет иначе — работать с точными формулами, как математик с ручкой.

SymPy — библиотека символьной (компьютерной) алгебры: она оперирует не числами, а математическими выражениями, давая точные ответы вроде √2, π/4, sin(x).

Две вселенные: число и символ

Когда вы пишете в обычном Python import math; math.sqrt(2), получаете 1.4142135623730951 — приближение из 17 цифр. Корень из двух иррационален, точно его в float не записать. SymPy идёт другим путём: для него √2 — это объект «корень из двойки», точный, без потери цифр. Возведите его в квадрат — получите ровно 2, а не 2.0000000000000004.

АспектЧисленно (SciPy)Символьно (SymPy)
√21.4142135623730951sqrt(2) — точно
1/30.33333333333333331/3 — точная дробь
Производная x²нет понятия2·x — формула
Скоростьочень быстромедленно

Зачем это нужно

Символьная математика делает то, что раньше требовало математика с бумагой: выводит формулы, берёт производные и интегралы, решает уравнения «в буквах», упрощает громоздкие выражения. Результат — не число, а формула, в которую потом можно подставить любые значения. Это бесценно, когда нужна точность (криптография, теория чисел), вывод аналитических зависимостей или проверка ручных выкладок.

Точные дроби — это умеет даже stdlib

Чтобы прочувствовать идею «точно, а не приближённо», начнём с того, что есть прямо в Python — модуль fractions. Сложим 1/10 + 2/10 точно:

from fractions import Fraction

a = Fraction(1, 10)
b = Fraction(2, 10)
print("Точно   :", a + b)             # 3/10, без округления

# а вот как это выглядит в обычном float:
print("Приближ.:", 0.1 + 0.2)         # 0.30000000000000004

Вывод:

Точно   : 3/10
Приближ.: 0.30000000000000004

Видите разницу? float дал 0.30000...004 вместо ровного 0.3 — накопилась ошибка округления (0.1 и 0.2 не представимы точно в двоичной системе). Fraction хранит числитель и знаменатель целыми и считает точно. SymPy делает то же самое, но не только с дробями, а с любыми математическими выражениями — переменными, функциями, корнями.

Как выглядит SymPy

В SymPy выражения строятся из символов (код для чтения, в браузере не исполняется — нужна сторонняя библиотека):

import sympy as sp

x = sp.Symbol("x")
expr = (x + 1)**2

print(sp.expand(expr))    # x**2 + 2*x + 1 — раскрыли скобки точно
print(sp.sqrt(8))         # 2*sqrt(2) — упростил корень точно
print(sp.Rational(1, 3) + sp.Rational(1, 6))  # 1/2

Как работает под капотом

SymPy представляет каждое выражение как дерево. Запись x**2 + 2*x + 1 внутри — это узел «сложение» с тремя ветвями: «x в степени 2», «2 умножить на x» и «1». Любая операция (упрощение, дифференцирование) — это обход и перестройка этого дерева по правилам математики. Числа SymPy хранит как точные объекты: целые произвольной длины, рациональные дроби, символические константы (π, e). Именно поэтому SymPy медленный — он не считает в «железных» float, а манипулирует структурами в памяти Python. Зато не теряет ни бита точности.

Когда что брать

  • SymPy — вывести формулу, взять производную/интеграл «в буквах», решить уравнение точно, проверить выкладки.
  • SciPy/NumPy — посчитать много чисел быстро, обработать данные, симуляции.
  • Часто их связывают: SymPy выводит формулу, lambdify превращает её в быструю численную функцию (об этом — позже).

Частые ошибки

  • Ждать от SymPy скорости NumPy. Символьные вычисления на порядки медленнее — не гоняйте их в циклах по миллиону точек.
  • Писать 1/3 вместо Rational(1,3). Обычное деление в Python даст float и «заразит» всё выражение приближением.
  • Путать символ и переменную Python. x = Symbol('x') создаёт математический символ; имя переменной Python и имя символа — разные вещи.

Итог

  • SymPy работает с точными формулами, а не приближёнными числами.
  • √2, 1/3, производная — для SymPy это точные объекты, а не float.
  • Идею «точно» демонстрирует даже stdlib-модуль fractions.
  • Под капотом — деревья выражений; цена точности — скорость.
Проверьте себя
1. В чём главное отличие SymPy от SciPy/NumPy?
ASymPy быстрее
BSymPy даёт точные символьные результаты (sqrt(2), 1/3, формулы), а не приближённые числа
CSymPy работает только с целыми
DМежду ними нет разницы
2. Почему 0.1 + 0.2 в обычном float даёт 0.30000000000000004, а не 0.3?
AОшибка интерпретатора
BЧисла с плавающей точкой не хранят 0.1 и 0.2 точно (двоичная система) — накапливается ошибка округления
CPython округляет вверх
DЭто особенность модуля fractions
3. Как SymPy хранит выражение x**2 + 2*x + 1 внутри?
AКак строку
BКак дерево выражения с узлами-операциями и ветвями-подвыражениями
CКак массив float
DКак байт-код