Большие числа в Python: типы данных и практические примеры
Числа с плавающей точкой (float) в Python 3 имеют ограниченную точность и не могут точно представить большие целые числа за пределами 53 бит мантиссы. Для работы с числами произвольного размера Python предоставляет несколько встроенных типов данных.
Целые числа произвольной точности (int)
Тип int в Python не имеет фиксированного лимита. Любое целое число, помещающееся в доступную оперативную память, может быть представлено и обработано.
Примеры:
# Огромное целое число
big = 10**100
print(big)
большие числа python 3 (большие числа в python 3)
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Операции с такими числами выполняются как обычно:
a = 2**1000
b = 2**500
c = a // b # целочисленное деление
print(c)
20769187434139310514121985316880384
Как избежать потери точности при делении больших целых чисел?
При делении с плавающей точкой (использование /) результат преобразуется в float, что может привести к потере точности. Для сохранения точности следует применять целочисленное деление // или модуль %.
x = 10**50
y = 3
# float деление
res_float = x / y
print(type(res_float), res_float)
# целочисленное деление
res_int = x // y
print(res_int)
<class 'float'> 3.3333333333333335e+49 33333333333333333333333333333333333333333333333333
Проблема: Умножение или возведение в степень очень больших чисел может потребовать много памяти и времени. Например, вычисление 2**10**7 может превысить доступную оперативную память.
Решение: Использовать итеративные алгоритмы, модульную арифметику или библиотеки для работы с большими числами (например, gmpy2). Для проверки размера числа можно применить sys.getsizeof().
import sys
big_num = 2**10000
print(sys.getsizeof(big_num), 'байт')
8548 байт
Как точно представить десятичные дроби без ошибок округления?
Для финансовых и других расчётов, где критична точность десятичных дробей, используется тип decimal.Decimal. Он хранит числа в десятичном представлении с задаваемой точностью.
from decimal import Decimal, getcontext
getcontext().prec = 50 # точность 50 знаков
d1 = Decimal('0.1')
d2 = Decimal('0.2')
print(d1 + d2)
print(0.1 + 0.2) # сравнение с float
0.3 0.30000000000000004
Decimal позволяет выполнять точные денежные расчёты, избегая накопления ошибок.
Проблема: Производительность Decimal ниже, чем у int и float. Кроме того, требуется явно задавать контекст точности.
Как хранить рациональные числа без потери точности при делении?
Тип fractions.Fraction представляет число в виде дроби (числитель и знаменатель). Любая операция с Fraction возвращает точный рациональный результат.
from fractions import Fraction
f = Fraction(1, 3)
g = Fraction(2, 5)
sum_f = f + g
print(sum_f)
print(float(sum_f))
11/15 0.7333333333333333
Fraction удобен для алгебраических операций и сравнений.
Проблема: При работе с большими числами знаменатели могут расти, и операции становятся медленнее. Для очень больших рациональных чисел лучше использовать целые числа или Decimal.
Как сравнивать очень большие числа с плавающей точкой?
При сравнении больших целых чисел с float может возникнуть ошибка из-за потери точности float. Функция math.isclose() позволяет сравнивать числа с заданной относительной или абсолютной погрешностью.
import math
big_int = 10**20
big_float = 1e20
print(big_int == big_float) # может быть False
print(math.isclose(big_int, big_float, rel_tol=1e-15))
False True
Использование isclose актуально при работе с результатами вычислений, где возможны небольшие расхождения.
Как повысить производительность работы с очень большими целыми числами?
Стандартный int Python использует алгоритмы с асимптотикой O(n^2) для умножения больших чисел. Сторонняя библиотека gmpy2 предоставляет тип mpz, реализующий более быстрые алгоритмы (например, умножение Карацубы).
# Установите gmpy2: pip install gmpy2
import gmpy2
a = gmpy2.mpz(2)**1000000
b = gmpy2.mpz(3)**500000
c = a * b
print(len(str(c))) # количество цифр результата
(длинное число)
gmpy2 также поддерживает работу с дробями и числами с плавающей точкой произвольной точности.
Проблема: gmpy2 не входит в стандартную библиотеку, требует установки и может не работать на некоторых платформах.
Расширенные примеры работы с большими числами
Вычисление факториала 1000
import math
print(math.factorial(1000))
402387260077093773543702433923... (сокращено)
Функция math.factorial использует эффективный итеративный алгоритм. Рекурсивное вычисление может быть медленнее и требует установки sys.setrecursionlimit, если глубина рекурсии велика.
Возведение в степень по модулю (модульная арифметика)
# Вычисление 2**1000 mod 1000
result = pow(2, 1000, 1000)
print(result)
376
Функция pow с тремя аргументами значительно эффективнее, чем (2**1000) % 1000, так как не создаёт огромное промежуточное число.
Генерация большого простого числа (512 бит)
import random
def is_prime(n, k=10):
# тест Миллера-Рабина
if n == 2 or n == 3:
return True
if n % 2 == 0:
return False
r, d = 0, n - 1
while d % 2 == 0:
r += 1
d //= 2
for _ in range(k):
a = random.randrange(2, n - 2)
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
# Генерация 512-битного простого числа
while True:
candidate = random.getrandbits(512)
if is_prime(candidate):
print('Найдено простое число:', candidate)
break
Найдено простое число: (512-битное число)
Для генерации больших простых чисел используйте модуль random.getrandbits и тест на простоту. В реальных приложениях лучше применять библиотеку sympy.
Точные десятичные вычисления с Decimal (вычисление числа e)
from decimal import Decimal, getcontext
getcontext().prec = 80
e = Decimal(1)
fact = Decimal(1)
for i in range(1, 100):
fact *= i
e += Decimal(1) / fact
print(e)
2.7182818284590452353602874713526624977572470936999595749669676277240766303535476
Используя Decimal, можно получать сколь угодно точные значения математических констант.
Сравнение производительности int и gmpy2
import gmpy2, time
# int
start = time.time()
a = 2**1000000
b = 3**200000
c = a * b
print('int time:', time.time() - start)
# gmpy2
start = time.time()
a = gmpy2.mpz(2)**1000000
b = gmpy2.mpz(3)**200000
c = a * b
print('gmpy2 time:', time.time() - start)
int time: 0.435 (gmpy2 time: 0.012)
Для очень больших чисел gmpy2 может быть значительно быстрее (в данном примере в 36 раз). Однако для большинства повседневных задач стандартный int достаточен.
Использование Fraction для точных вероятностей
from fractions import Fraction
import math
n = 100
k = 40
# Точная вероятность выпадения 40 орлов из 100 бросков монеты
prob = Fraction(math.comb(n, k), 2**n)
print('Точная дробь:', prob)
print('Приблизительное значение:', float(prob))
Точная дробь: 245810588801891098700/633825300114114700748351602688 Приблизительное значение: 0.00038779229467880653
Fraction позволяет хранить точное рациональное значение вероятности, что важно в научных расчётах.
Сложение больших чисел, представленных в виде строк
# Демонстрация преобразования строк в int и обратно
a_str = '1234567890' * 100000 # 10^6 цифр
b_str = '9876543210' * 100000
a = int(a_str)
b = int(b_str)
result = a + b
print('Количество цифр в результате:', len(str(result)))
Количество цифр в результате: 1000000
Преобразование длинных строк в int может потребовать много памяти, но Python справляется с числами размером до нескольких миллионов цифр.
Работа с большими числами в комбинаторике: количество сочетаний
from math import comb
n = 10**6
k = 500000
c = comb(n, k)
print('Число сочетаний C(10^6, 500000) имеет', len(str(c)), 'цифр')
(вывод количества цифр, около 300000)
math.comb для больших n, k также использует произвольную точность int.
Быстрое возведение в степень для больших чисел (бинарное возведение)
def fast_pow(base, exp):
result = 1
while exp:
if exp & 1:
result *= base
base *= base
exp >>= 1
return result
print(len(str(fast_pow(2, 10000)))) # количество цифр 2^10000
3011
Алгоритм бинарного возведения в степень (быстрое возведение) позволяет вычислить огромные степени за O(log exp) умножений.