Mathematic in python

Помимо встроенных типов чисел предоставляется дополнительные классы для вычислений с повышеннйо точностью или специальных вычислений.

decimal

Числа с фиксированной точностью и числа с плавающей точкой.

Десятичное число в данном модуле - это неизменяемый тип данных. Десятичное число может обладать знаком, состоять из мантисы и экспоненты. Для сохранения значимости конечные нули не усекаются.

Контекст вычислений определяет точность, правила округления, ограничения на экспоненты, флаги результатов операций и средства активации исключений.

Сигналы – это специальные условия, которые возникают в процессе вычислений и могут использоваться для вызова исключений.

Дробные десятичные числа представлены классом Decimal. Его конструктору передается одно целое число или строка. Можно использовать from_float() для точного преобразования числа с плавающей точкой. В последнем случае длина строки может быть значительной, так как некоторые числа с плавающей точкой невозможно точно представить в двоичной форме.

import decimal

>>> print(decimal.Decimal(5))
5

>>> print(decimal.Decimal('3.14'))
3.14

>>> print(decimal.Decimal(str(0.1)))
0.1

>>> print(decimal.Decimal.from_float(0.1))
0.1000000000000000055511151231257827021181583404541015625

Кроме того, дробное десятичное число можно задать как кортеж, содеоржащий флаг знака (0 для пложительных, 1 для отрицательных), кортеж цифр и целочисленную экспоненту.

>>> m = (1, (1, 1, 1), -4)
>>> print(decimal.Decimal(m))
-0.0111

Над decimal можно производить теже арифметические операции, что и над стандартными числовыми типами. При этом поддерживаются и арифметические операции с целочисленными типами… а вот числа с плавающей точкой должны быть предварительно преобразованы в decimal.

Decimal предоставляет специальные числовые типы Infinity, NaN и 0. Сравнение с NaN на равенство всегда возвращает False, а на неравенство - True. При этом сравниваться в целях сортировки нельзя - будет поднята ошибка. Оба типа могут иметь знак + и -.

>>> import decimal

>>> for value in ['Infinity', 'NaN', '0']:
>>>     print(decimal.Decimal(value), decimal.Decimal('-' + value))
Infinity -Infinity
NaN -NaN
0 -0

Полный синтаксис

sign           ::=  '+' | '-'
digit          ::=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
indicator      ::=  'e' | 'E'
digits         ::=  digit [digit]...
decimal-part   ::=  digits '.' [digits] | ['.'] digits
exponent-part  ::=  indicator [sign] digits
infinity       ::=  'Infinity' | 'Inf'
nan            ::=  'NaN' [digits] | 'sNaN' [digits]
numeric-value  ::=  decimal-part [exponent-part] | infinity
numeric-string ::=  [sign] numeric-value | [sign] nan

Способ округления и точность decimal можно задать с помощью контекста

>>> import decimal
>>> decimal.getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[Overflow, DivisionByZero,
        InvalidOperation])

>>> decimal.getcontext().prec = 7       # Set a new precision

Точность задается через атрибут prec, который можно установить в диапазоне от 1 до decimal.MAX_PREC

Округление задается через атрибут rounding. Доступно несколько опций:

Подробнее

Что-бы не менять весь контекст, контекст можно задать локально через менеджера:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

Кроме того, контексты можно использовать для создания экземпляра Decimal, который унаследует все вводные, взятые из контекста.

>>> import decimal

>>> c = decimal.getcontext().copy()
>>> c.prec = 3
>>> pi = c.create_decimal('3.1415')

>>> print('PI:', pi)
PI: 3.14
>>> print('RESULT:', decimal.Decimal('2.01') * pi)
RESULT: 6.3114

На самом деле глобальный контекст в decimal является локальным контекстом потока, поэтому каждый поток может конфигурироваться с использованием разных значений. Кроме того, с помощью сигналов можно устанавливать недопустимые операции, при выполнении которых будет поднята ошибка.

Пример с тредами на gists:

fractions

Определяет операции, осуществляемые над числами из множества рациональных чисел. Апи определено классом Rational в модуле numbers. fractions полностью поддерживает все математические выражения

Есть несколько способов создания fractions. Во первых непосредственно задав числитель и знаменатель

>>> from fractions import Fraction

>>> f = Fraction(1, 4)
>>> print(f)
1/4

Второй вариант - использование строкового представлениия “числитель/знаменатель”. Поддерживаются также любые строки, которые могут быть преобразованы во float()

>>> f = Fraction('1/4')
>>> f = Fraction('0.5')
>>> f = Fraction('5e-3')

Кроме того можно создавать экземпляры Fraction непосредственно из float или Decimal. При этому потери точности, как в Decimal уже нет. Однако значения с плавающей точкой, которые не могут быть выражены точно в Decimal могут привести к очень большим дробям во Fraction.

>>> f = Fraction(0.1)
>>> print(f)
3602879701896397/36028797018963968

Есть способ этим управлять, устанавливая предельный размер значения знаменателя

>>> print(f.limit_denominator(10))
1/10

random

Модуль предоставялет генератор псевдослучайных чисел. основанный на Mersenne twister алгоритме. Числа, получаемые таким методом, подчиняются равномерному распределению с большим периодом повторения.

Функция random() возвращает следующее число из генерируемой последовательности. Все значения последовательности принадлежат [0, 1.0). Естественно повторные запуски приводят к разным последовательностям.

uniform() позволяет сгенерировать число из выбранного интервала. В данном случае просто происходит корректировка возврата из random() по формуле min + (max - min) * random

>>> import random
>>> print(random.uniform(1, 100))
25.113356610778503

Чтобы не получать каждый раз разные последовательности случайных чисел, генератор можно инициализировать функцией seed(). Сид влияет на генерацию первого числа последовательности, а с учетом ее детерменированности, по сути на всю последовательность. Если сид не задан, используется фактор определяющий случайность, специфичный для платформы или, если такого нет, системное время.

Кроме того, состояние генератора случайных чисел можно сериализовать. При загрузке состояния, оно будет выступать в качестве начальтной точки для продолжения процесса генерации, чято снижает вероятность получения ранее полученных значений. Все это делается с помощью getstate() и setstate()

Случайное целое число можно получить с помощью randint(), указав соответствующий диапазон генерации. Числа могут быть положительные и отрицательные, первое меньше второго. randrange() реализует выбор из диапазона с шагом.

choice() позволяет выбрать случайный элемент из последовательности. См. статью [random-choice]

>>> import random

>>> outcomes = {
...     'heads': 0,
...     'tails': 0,
... }
>>> sides = list(outcomes.keys())

>>> for i in range(10000):
    outcomes[random.choice(sides)] += 1

>>> print('Heads:', outcomes['heads'])
Heads: 4932
>>> print('Tails:', outcomes['tails'])
Tails: 5068

Функция shuffle() реализует случайный выбор с перемешиванием на месте. sample() - реализует случайную выборку без возврата (получается список элементов заданной длины без повторов). Работает только с хешируемыми объектами и, соответственно, исходную последовательность не изменяет.

Инициалищзировав класс Random можно задать несколько состояний нескольких генераторов и управлять процессами рандомизации по отдельности. Использование системного времени ожидаемо привело к генерации одного и того же значения.

>>> import random
>>> import time

>>> seed = time.time()
>>> r1 = random.Random(seed)
>>> r2 = random.Random(seed)

>>> for i in range(3):
...     print('{:04.3f}  {:04.3f}'.format(r1.random(), r2.random()))
0.481  0.481
0.911  0.911
0.324  0.324

Можно получить доступ к системному генератору случайных чисел через класс SystemRandom

>>> import random
>>> import time

>>> seed = time.time()
>>> r1 = random.SystemRandom(seed)
>>> r2 = random.SystemRandom(seed)

>>> for i in range(3):
...     print('{:04.3f}  {:04.3f}'.format(r1.random(), r2.random()))
0.379  0.351
0.625  0.157
0.508  0.405

В данном слкчае seed() уже ничего не задает, т.к. используется системный рандомизатор.

Наконец, random включает несколько функций, позволяющих получать случайные числа не только из однородного распределения:

подробнее смотри тут

math

Математические константы и функции. Модуль поставляет ряд собственных констант, в т.ч. определяющих бесконечные числа (inf), возникающие в результате переполнения разрядов и неопределенное значение (nan).

>>> import math

>>> print('{:.30f}'.format(math.pi))
3.141592653589793115997963468544
>>> print('{:.30f}'.format(math.e))
2.718281828459045090795598298428
>>> print('{:.30f}'.format(math.nan))
nan
>>> print('{:.30f}'.format(math.inf))
inf

Часть операций приводят не к значениям inf, а к ошибке OverflowError - это связано с реализацией на C. Деление с участием бесконечных значений приводит к nan, который не равен никакому другому значени в т.ч. и себе. Самый простой способ проверить - функции isnan() и isfinite().

Сравнение чисел с плавающей точкой подвержены ошибкам представления числа. С помощью isclose() можно минимизировать вероятность ошибки представления. Этот метод позволяет стравнивать с определенным допуском и. естественно, не гарантирует точное соответствие. nan не может быть близким ни к одному значению, вклюяая самого себя. inf близок только к самому себе.

Числа с плавающей точкой можно преобразовать в целые трямя способами. trunc() отбрасывает дробную часть (эквивалентно непосредственному преобразованию). floor() округляет до наибольшего из предшествующих целых, ceil() до наименьшего.

modf() преобразует число с плавающей точкой в кортеж, где первое значение - цклая часть, а второе - дробная. Оба числа в кортеже - с плавающей точкой.

frexp() возвращает мантиссу и экспоненту числа с плавающей точкой: x=m*2^e - возвращается m и e. Это можно использовать для переноса значений между системами. ldexp() осуществляет обратное преобразование.

fabs() возвращает абсолютное значение (число без знака). copysign() возвращает знак числа. что полезно для сравнения с nan, у которого тоже может быть знак.

fsum() реализуют более точный алгоритм вычисления суммирования над числами с плавающей точкой.

factorial() повзволяет вычислить факториал для целых чисел или для чисел с плавающей точкой, если те могут быть преобразованы в целые без потери точности.

gamma() считает гаммафункцию, а lgamma() ее натуральный логарифм.

fmod() предоставляет более точную реализацию деления по модулю, чем % для чисел с плавающей точкой. Знак результата может отдличаться из-ща отличий в реализации.

gcd() наибольший общий делитель

pow() возводит в степень (возвращает float). sqrt() отдельно реализует квадратный корень. При этом math не работает с комплексными числами и корень из отрицательного числа ведет к ValueError

log() натуральный логарифм. log10() реализует более точный алгоритм для десятичного логарифма. log2() для логарифма по основанию 2. log1p() вычисляет ряд Ньютона-Меркатора и более точен для значений, близких к 0.

exp() экспонента. exp1p() возвращает e^(x)-1

Градусы переводятся в радианы с помощью radians(). Обратно - degrees(). Тригонометрические функции (в радианах): sin(), cos(), tan(). Гипотянуза hypot(). Обратные ф-ии: asin(), acos(), atan(). Гиперболические функции: sing(), cosh(), tanh(), asing(), acosh(), atanh().

Реализовано несколько специальных функций. Гауссова ф-ия ошибок erf() и erfc() (1 - erf).

В модуле statistics реализованы статистические функции для int, float, Decimal, Fraction: mode(), mean(), median() (median_low(), median_high() для устранения скошенности статистики из-за числа элементов набора данных), median_grouped(), а также методы для расчета дисперсии и мат.ожидания для генеральной совокупности и выборок. Еще можно считать корреляции. ковариации и лин.регрессию. Подробнее тут

Документация модуля math. Некоторео кол-во функций добавлено после 3.8

Смотри еще:

>>> На главную