Numbers
int, float and friends in Python 3.14: literals, division and modulo rules, unbounded integers,
bits, float traps, math and number theory, modular arithmetic, parsing and formatting, costs, and
the integer idioms DS&A solutions are built from. The JS side is in
Coming from JavaScript.
Types & literals
| Type | Literals | Notes |
|---|---|---|
int | 42, -7, 1_000_000, 0xFF, 0o17, 0b1011 | unbounded: never overflows |
float | 3.14, 1e9, 2.5e-3, float("inf"), math.inf | IEEE 754 double, like a JS number |
bool | True, False | a subclass of int: True + True == 2, sum(flags) counts Trues |
complex | 3 + 4j | abs(3 + 4j) == 5.0; rare in DS&A |
fractions.Fraction | Fraction(1, 3) | exact rationals: Fraction(1, 3) + Fraction(1, 6) == Fraction(1, 2) |
decimal.Decimal | Decimal("0.1") | exact decimal digits for money; build it from a str, not a float |
type(10**9), type(1e9) # (<class 'int'>, <class 'float'>)
1e9 == 10**9 # True, but 1e9 is a float: use 10**9
int(" 42 "), int("1_000"), int("-7") # (42, 1000, -7)
int("١٢٣") # 123: any Unicode decimal digits parse
int(3.99), int(-3.99) # (3, -3): truncates toward 0
True + True, isinstance(True, int) # (2, True)
{1: "a", 1.0: "b", True: "c"} # {1: 'c'}: one key1 == 1.0 == True and they hash alike, so they collide as dict keys and set members. Write 10**9,
not 1e9, wherever an int is expected (range(1e9) is a TypeError).
Arithmetic & division
| Expression | Result | Rule |
|---|---|---|
7 / 2, 6 / 3 | 3.5, 2.0 | / always returns a float |
7 // 2, -7 // 2, 7 // -2 | 3, -4, -4 | // floors toward minus infinity (C and JS truncate) |
-7 % 3, 7 % -3 | 2, -2 | % takes the divisor's sign, so a == (a // b) * b + a % b |
divmod(-7, 2) | (-4, 1) | (a // b, a % b) in one call |
math.fmod(-7, 3) | -1.0 | C-style remainder (sign of the dividend), float result |
-(-7 // 2) | 4 | ceiling division, exact for any int size |
2 ** 10, 2 ** -1 | 1024, 0.5 | a negative exponent gives a float |
-2 ** 2 | -4 | ** binds tighter than unary minus: write (-2) ** 2 |
2 ** 3 ** 2 | 512 | ** is right-associative: 2 ** 9 |
pow(2, 10, 1000) | 24 | modular power, fast for huge exponents |
abs(-5), max(3, 7), min(...) | 5, 7 | max/min take an iterable or several arguments |
round(2.5), round(3.5), round(-0.5) | 2, 4, 0 | round half to even (banker's rounding) |
round(1250, -2), round(2.675, 2) | 1200, 2.67 | negative digits round to tens, hundreds; 2.675 is really 2.67499... |
a, b = -7, 2
q, r = divmod(a, b) # (-4, 1)
assert q * b + r == a # always holds
# truncating division (C, Java, JS Math.trunc), exactly
def tdiv(a: int, b: int) -> int:
q = abs(a) // abs(b)
return q if (a < 0) == (b < 0) else -q
tdiv(-7, 2), int(-7 / 2) # (-3, -3)
int(10**30 / 7) == 10**30 // 7 # False: / went via float1/6
-7 / 2 is -3.5, between -4 and -3. Integer division has to pick one of them.
Unbounded integers
An int grows as needed, so 2**1000 is exact and nothing ever wraps around. Problems written for
32-bit languages expect you to detect or simulate overflow yourself.
| Need | Python |
|---|---|
| 32-bit range check | -2**31 <= x <= 2**31 - 1 (-2**31 is -(2**31), the right value) |
| wrap to signed 32-bit | (x + 2**31) % 2**32 - 2**31 |
| wrap to unsigned 32-bit | x & 0xFFFFFFFF |
| wrap to signed 64-bit | (x + 2**63) % 2**64 - 2**63 |
| number of bits | x.bit_length(): (255).bit_length() == 8, (0).bit_length() == 0 |
| the biggest int | there isn't one; sys.maxsize (2**63 - 1 on 64-bit) is the largest container size, not an int limit |
import sys
INT_MAX = 2**31 - 1
x = INT_MAX + 1 # 2147483648: no overflow
(x + 2**31) % 2**32 - 2**31 # -2147483648: wrapped
sys.maxsize + 1 # 9223372036854775808: still fine
sys.get_int_max_str_digits() # 4300
try:
str(7**6000) # computing 7**6000 is fine; printing isn't
except ValueError as e:
print(type(e).__name__) # ValueError
len(hex(7**6000)), (7**6000).bit_length() # (4214, 16845)
sys.set_int_max_str_digits(0) # 0 = no limit
len(str(7**6000)) # 5071is compares identity, not value. CPython caches the ints from -5 to 256, so
int("256") is int("256") is True and int("257") is int("257") is False. Compare numbers with
==.
Bits
| Operator | Meaning | Python detail |
|---|---|---|
a & b, a | b, a ^ b | and, or, xor | bind tighter than ==: n & 1 == 0 means (n & 1) == 0, unlike C and JS |
~x | not | -x - 1: ~5 == -6 |
x << k | shift left | exact: 1 << 100 is a 101-bit int |
x >> k | shift right | floors: -7 >> 1 == -4, -1 >> 10 == -1; there is no >>> |
x.bit_count() | number of 1 bits (3.10+) | counts the bits of abs(x): (-5).bit_count() == 2 |
x.bit_length() | bits needed without the sign | n.bit_length() - 1 is ⌊log₂ n⌋ for n > 0 |
bin(x), format(x, "b") | '0b101', '101' | bin(-5) is '-0b101': sign and magnitude, not two's complement |
f"{x:08b}", f"{x:#x}" | zero-padded binary, 0x hex | f"{5:08b}" == "00000101" |
int(s, 2) | parse binary | int("1011", 2) == 11, int("0b1011", 0) == 11 |
Negative ints behave as if they had infinitely many leading 1 bits. Mask with & 0xFFFFFFFF to
see a fixed-width two's complement pattern, and convert back to signed by hand:
MASK = 0xFFFFFFFF
x = -5
x & MASK # 4294967291
format(x & MASK, "032b")[-8:] # '11111011'
def to_signed32(u: int) -> int:
u &= MASK
return u - (1 << 32) if u & (1 << 31) else u
to_signed32(0xFFFFFFFF), to_signed32(2**31)
# (-1, -2147483648)
-7 >> 1, ~5, 6 & -6 # (-4, -6, 2): lowest set bitThe tricks themselves (lowest set bit, submasks, xor cancellation, popcount DP) are in Bit manipulation. Two of them, bit by bit:
1/6
x = 12 is 0b1100. Its lowest set bit is bit 2.
Floats
A float is an IEEE 754 double: 53 bits of mantissa, so every integer up to 2**53 is exact and
most decimal fractions are not.
| Expression | Result | Why |
|---|---|---|
0.1 + 0.2 | 0.30000000000000004 | binary fractions; compare with math.isclose |
math.isclose(0.1 + 0.2, 0.3) | True | defaults rel_tol=1e-09, abs_tol=0.0 |
math.isclose(1e-10, 0) | False | relative tolerance is useless near 0: pass abs_tol=1e-9 |
float(2**53 + 1) == 2**53 | True | past 2**53 floats skip integers |
int(1e23) | 99999999999999991611392 | 1e23 was never exactly 10²³ |
float(10**400), 10**400 / 3 | OverflowError | an int too big for a float |
math.inf > 10**1000 | True | inf compares above every int |
math.inf - math.inf, math.inf * 0 | nan | |
math.nan == math.nan | False | test with math.isnan(x) |
max(1, math.nan), max(math.nan, 1) | 1, nan | NaN poisons comparisons, order-dependent |
int(math.inf), int(math.nan) | OverflowError, ValueError | |
math.floor(-2.5), math.ceil(-2.5), math.trunc(-2.5) | -3, -2, -2 | all three return an int |
(2.0).is_integer() | True | int.is_integer() exists too (3.12+) |
sum([0.1] * 10) | 1.0 | compensated summation since 3.12 (3.11 gave 0.9999999999999999) |
math.fsum(xs) | accurate total | tracks exact partial sums; the docs allow a rare last-bit error on some builds |
sys.float_info.epsilon | 2.220446049250313e-16 | 2**-52, the gap after 1.0 |
import math
from fractions import Fraction
(0.1).as_integer_ratio()
# (3602879701896397, 36028797018963968)
Fraction("0.1") + Fraction("0.2") == Fraction("0.3") # True
math.isclose(1e-10, 0, abs_tol=1e-9) # True
10**16 / 3 == (10**16 + 1) / 3 # True: different, same floatBinary search over floats: loop a fixed number of times (for _ in range(100):) instead of
while hi - lo > eps, which spins forever when eps is smaller than the gap between adjacent floats
at that magnitude (about 1.2e-7 near 10**9).
math & number theory
| Call | Result | Notes |
|---|---|---|
math.gcd(12, 18, 24) | 6 | any number of arguments (3.9+); gcd(0, 5) == 5; always non-negative |
math.lcm(4, 6, 10) | 60 | 3.9+ |
math.isqrt(n) | ⌊√n⌋, exact | 3.8+; ValueError for n < 0; ceiling ⌈√n⌉ is 1 + isqrt(n - 1) for n > 0 |
math.comb(5, 2), math.perm(5, 2) | 10, 20 | exact ints; comb(3, 5) == 0 |
math.factorial(5) | 120 | |
math.prod([1, 2, 3, 4]) | 24 | prod([]) == 1 |
math.sumprod([1, 2], [3, 4]) | 11 | dot product (3.12+) |
pow(b, e, m) | b**e % m | O(log e) multiplications |
pow(a, -1, m) | modular inverse | 3.8+; ValueError when gcd(a, m) != 1 |
math.log2(x), math.log10(x) | float logs | inexact for big ints: use bit_length or len(str(n)) |
math.log(x, 2) | float | worse than log2: math.log(2**29, 2) != 29 |
math.dist(p, q), math.hypot(dx, dy) | Euclidean distance |
import math
math.isqrt(10**30 + 1) # 1000000000000000
n = 10**34 - 1
int(n**0.5), math.isqrt(n)
# (100000000000000000, 99999999999999999)
math.log2(2**50 - 1), (2**50 - 1).bit_length() - 1
# (50.0, 49): only bit_length is right
pow(38, -1, 97) # 23, since 38 * 23 % 97 == 1
math.comb(52, 5) # 2598960 poker handsModular arithmetic
"Return the answer modulo 10⁹ + 7" keeps answers small; in Python it also keeps intermediate
numbers small, which keeps them fast. Reduce after every + and *.
| Rule | Python |
|---|---|
| sum, product | (a + b) % MOD, a * b % MOD |
| difference | (a - b) % MOD: already in [0, MOD), no + MOD needed |
| power | pow(a, e, MOD), never a**e % MOD |
division by b | multiply by pow(b, -1, MOD); for a prime MOD also pow(b, MOD - 2, MOD) (Fermat) |
| constant | MOD = 10**9 + 7; 10e9 + 7 is the float 10000000007.0 |
MOD = 10**9 + 7
(3 - 5) % MOD # 1000000005: non-negative already
pow(2, 10**18, MOD) # 719476260, instantly
inv3 = pow(3, -1, MOD)
6 * inv3 % MOD # 2: "6 / 3" mod p
pow(3, MOD - 2, MOD) == inv3 # True: FermatCounting DP with large answers (paths, ways to climb, subsets) reduces each table cell; precomputed
factorials give nCr % p in O(1) per query (recipe below).
Parsing & formatting
| Call | Result | Notes |
|---|---|---|
int("ff", 16), int("z", 36) | 255, 35 | bases 2–36 |
int("0x1f", 0) | 31 | base 0 reads the prefix like a literal; rejects "010" |
int("12px"), int(""), int("1.5") | ValueError | the whole string must be an integer (JS parseInt stops at junk) |
float(" 1.5 "), float("1e3"), float("-inf") | 1.5, 1000.0, -inf | |
bin(10), oct(8), hex(255) | '0b1010', '0o10', '0xff' | bin(10)[2:] or format(10, "b") without the prefix |
f"{1234567:,}", f"{1234567:_}" | '1,234,567', '1_234_567' | grouping |
f"{3.14159:.2f}", f"{0.5:.0%}", f"{1e-7:.2e}" | '3.14', '50%', '1.00e-07' | fixed, percent, scientific |
f"{42:>5}", f"{-42:05d}" | ' 42', '-0042' | width and zero padding |
str(n), [int(d) for d in str(n)] | digits as text or ints | 4300-digit limit applies |
n.to_bytes(4, "big"), int.from_bytes(b, "big") | bytes ↔ int | signed=True for two's complement |
The full format-spec grammar is on the Strings sheet, next to f-strings.
n = 9045
[int(d) for d in str(n)] # [9, 0, 4, 5]
sum(map(int, str(n))) # 18
digits: list[int] = []
m = n
while m:
m, d = divmod(m, 10)
digits.append(d) # least significant first
print(digits) # [5, 4, 0, 9]
int("".join(map(str, [9, 0, 4, 5]))) # 9045Costs
| Operation | Cost | Notes |
|---|---|---|
+, -, *, //, %, comparisons on ordinary ints | O(1) in practice | an int is stored in 30-bit "digits"; a 64-bit value is 3 of them |
+, - on d-digit ints | O(d) | |
* on d-digit ints | O(d²), then Karatsuba O(d^1.585) | Karatsuba once both sides pass 70 internal digits (about 630 decimal digits) |
pow(b, e, m) | O(log e) multiplications | of numbers below m |
str(n), int(s) (base 10) | superlinear | hence the 4300-digit cap; bases 2, 4, 8, 16, 32 are linear |
math.isqrt(n) | exact, Newton's method on ints | n ** 0.5 is a float: wrong past 2**53, OverflowError past ~10³⁰⁸ |
math.factorial, math.comb | fast C code | still big ints: comb(10**5, 5 * 10**4) has 30,101 digits |
import sys
sys.int_info.bits_per_digit # 30
sys.getsizeof(1), sys.getsizeof(2**30) # (28, 32)
sys.getsizeof(10**100) # 72: 12 digits of 30 bitsEvery int is a heap object with a header, so a list of a million ints costs pointers plus objects;
array.array("q") or NumPy stores raw 8-byte values (see
Lists for container choices).
DS&A idioms
The integer moves problem solutions use, and the trap in each. The patterns are in the DS&A sheets (linked in the last column).
| Job | Python | Watch out | More |
|---|---|---|---|
| Sentinels | best = math.inf, worst = -math.inf | a float: int(math.inf) raises OverflowError; for an int sentinel use sys.maxsize | Lists |
| 32-bit overflow | if not -2**31 <= x <= 2**31 - 1: return 0 | ints never overflow, so check by hand where the problem says 32-bit | Python gotchas |
| Ceiling division | -(-a // b), or (a + b - 1) // b for a >= 0 | math.ceil(a / b) goes through a float | |
| Midpoint | mid = (lo + hi) // 2; upper mid (lo + hi + 1) // 2 | no overflow to dodge; // floors, so negative ranges still work | Binary search |
| Search on the answer | lo, hi = 0, 10**9 then shrink | integers: exact; floats: a fixed number of iterations | On the answer |
| Parity | n % 2, n & 1 | both give 1 for odd negatives (-3 % 2 == 1; JS gives -1) | |
| Power of two | n > 0 and n & (n - 1) == 0, or n.bit_count() == 1 | & binds tighter than == in Python, so no parentheses needed | Bit manipulation |
| Bitmask subsets | for m in range(1 << n):, test m >> i & 1 | 2ⁿ masks: only for n up to about 20 | Input size |
| Grid index ↔ cell | r, c = divmod(i, cols), i = r * cols + c | cols, not rows, is the divisor | Grids as graphs |
| Digits | while n: n, d = divmod(n, 10) or map(int, str(n)) | take abs(n) first: -123 % 10 == 7 | Fast & slow pointers |
| Slopes, ratios | g = gcd(dx, dy), key (dx // g, dy // g) with one sign rule | float slopes merge different lines and split equal ones | |
| Answers mod p | MOD = 10**9 + 7, reduce after every step | 10e9 + 7 is a float, and the wrong number | |
| Fast power | pow(b, e, MOD) | b ** e % MOD builds the whole huge power first | |
| Divisor loops | for d in range(1, math.isqrt(n) + 1): | int(n ** 0.5) is off by one for big n | |
| Perfect square | math.isqrt(n) ** 2 == n | ||
| Missing number | n * (n + 1) // 2 - sum(xs) or xor everything | no overflow worry, unlike in Java/C++ | Index as hash |
| Float equality | math.isclose(a, b, abs_tol=1e-9) | default abs_tol=0.0: isclose(x, 0) is False for every x != 0 | |
| Floor log | n.bit_length() - 1 (base 2), len(str(n)) - 1 (base 10) | math.floor(math.log10(999999999999999)) is 15, not 14 | |
| Average | (a + b) // 2 floors, (a + b) / 2 is a float |
import math
from math import gcd
rows, cols = 3, 4
r, c = divmod(7, cols) # (1, 3): cell of index 7
r * cols + c # 7: and back
n = 36
divisors = sorted(
d
for i in range(1, math.isqrt(n) + 1)
if n % i == 0
for d in {i, n // i}
)
print(divisors) # [1, 2, 3, 4, 6, 9, 12, 18, 36]
dx, dy = -4, 6
g = gcd(dx, dy) # 2: gcd is never negative
key = (dx // g, dy // g) # (-2, 3)
if key[0] < 0: # one sign convention per slope
key = (-key[0], -key[1])
print(key) # (2, -3)
-(-7 // 2), 8 & 7 == 0, (-3) % 2 # (4, True, 1)Coming from JavaScript
The TypeScript version of this sheet is Numbers.
| JS | Python |
|---|---|
number (double), BigInt | int (exact, unbounded) and float (double); no n suffix |
Number.MAX_SAFE_INTEGER (2**53 - 1) | no limit for int; floats lose integers past 2**53 the same way |
Math.floor(a / b) | a // b |
Math.trunc(a / b) | int(a / b) while it fits a float; exact: tdiv above |
a % b (sign of a: -7 % 3 === -1) | math.fmod(a, b); Python's % gives 2 |
Math.round(2.5) === 3, Math.round(-2.5) === -2 | round(2.5) == 2, round(-2.5) == -2: half to even |
(0.125).toFixed(2) === "0.13" | f"{0.125:.2f}" == "0.12" |
parseInt("12px") === 12, Number("") === 0 | int("12px") and int("") raise ValueError |
parseInt("ff", 16), (255).toString(2) | int("ff", 16), format(255, "b") |
Number.isInteger(x) | isinstance(x, int), or x.is_integer() for a float |
1 << 31 === -2147483648, x >>> 0 | 1 << 31 == 2147483648; x & 0xFFFFFFFF (no >>>) |
Infinity, NaN, isNaN(x) | math.inf, math.nan, math.isnan(x) |
Number.EPSILON | sys.float_info.epsilon |
Math.max(...xs) | max(xs); max([]) raises unless default= |
x ** y, Math.pow(x, y) | x ** y, pow(x, y), pow(x, y, m) |
Math.sqrt(n) | math.sqrt(n) (float), math.isqrt(n) (exact int) |
Math.random() | random.random() in [0, 1) |
Math.floor(Math.random() * n) | random.randrange(n); random.randint(a, b) includes b |
Recipes
Sieve of Eratosthenes
When you need every prime up to n (counting primes, prime factors of many numbers).
import math
def primes_upto(n: int) -> list[int]:
if n < 2:
return []
is_p = bytearray([1]) * (n + 1) # 1 byte per number
is_p[0] = is_p[1] = 0
for p in range(2, math.isqrt(n) + 1):
if is_p[p]: # cross off p*p, p*p + p, ...
hits = range(p * p, n + 1, p)
is_p[p * p :: p] = bytes(len(hits))
return [i for i, flag in enumerate(is_p) if flag]
print(primes_upto(30))
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
print(len(primes_upto(10**6))) # 78498O(n log log n) time, n bytes of memory. The slice assignment zeroes a whole stride in C instead of a Python loop.
Prime factorization
When you need the prime factors of one number (divisor counts, gcd tricks).
def factorize(n: int) -> dict[int, int]:
factors: dict[int, int] = {}
d = 2
while d * d <= n:
while n % d == 0:
factors[d] = factors.get(d, 0) + 1
n //= d
d += 1 if d == 2 else 2 # 2, then odd numbers
if n > 1: # what is left is prime
factors[n] = factors.get(n, 0) + 1
return factors
f = factorize(360)
print(f) # {2: 3, 3: 2, 5: 1}
count = 1
for e in f.values():
count *= e + 1
print(count) # 24 divisorsO(√n). Many queries up to the same limit: sieve the smallest prime factor of every number once instead.
nCr mod p
When a counting problem needs many binomial coefficients modulo a prime.
MOD = 10**9 + 7
N = 10**5
fact = [1] * (N + 1)
for i in range(1, N + 1):
fact[i] = fact[i - 1] * i % MOD
inv_fact = [1] * (N + 1)
inv_fact[N] = pow(fact[N], -1, MOD) # one inverse
for i in range(N, 0, -1): # the rest by stepping down
inv_fact[i - 1] = inv_fact[i] * i % MOD
def ncr(n: int, r: int) -> int:
if not 0 <= r <= n:
return 0
top = fact[n] * inv_fact[r] % MOD
return top * inv_fact[n - r] % MOD
print(ncr(5, 2), ncr(10**5, 5 * 10**4)) # 10 149033233O(N) setup, O(1) per query. math.comb(n, r) % MOD gives the same answer but builds the exact,
enormous binomial first.
Convert to any base
When a problem asks for base 7, a binary string, or a short id in base 36.
ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"
def to_base(n: int, base: int) -> str:
if not 2 <= base <= 36:
raise ValueError("base must be in 2..36")
if n == 0:
return "0"
sign = "-" if n < 0 else ""
n = abs(n)
out: list[str] = []
while n:
n, r = divmod(n, base)
out.append(ALPHABET[r])
return sign + "".join(reversed(out))
print(to_base(100, 7), to_base(-10, 2)) # 202 -1010
print(int(to_base(123456789, 36), 36)) # 123456789int(s, base) parses any of these back. Bases 2, 8 and 16 are built in: format(n, "b"), "o",
"x".
Reverse Integer with a 32-bit clamp
When a problem written for fixed-width ints wants 0 on overflow.
INT_MIN, INT_MAX = -(2**31), 2**31 - 1
def reverse_int(x: int) -> int:
n, r = abs(x), 0 # abs first: -123 % 10 == 7
while n:
n, d = divmod(n, 10)
r = r * 10 + d
r = -r if x < 0 else r
return r if INT_MIN <= r <= INT_MAX else 0
print(reverse_int(-123), reverse_int(120)) # -321 21
print(reverse_int(1534236469)) # 0: 9646324351 overflowsThe string version, int(str(abs(x))[::-1]) with the sign put back, gives the same results.
Exact slopes: points on a line
When grouping points by direction; float slopes merge or split lines at large coordinates.
from collections import Counter
from math import gcd
def direction(dx: int, dy: int) -> tuple[int, int]:
g = gcd(dx, dy) # > 0 unless both are 0
dx, dy = dx // g, dy // g
if dx < 0 or (dx == 0 and dy < 0):
dx, dy = -dx, -dy # one sign per direction
return dx, dy
def max_points(pts: list[tuple[int, int]]) -> int:
best = min(len(pts), 1)
for i, (x1, y1) in enumerate(pts):
dirs = Counter(
direction(x2 - x1, y2 - y1)
for x2, y2 in pts[i + 1 :]
)
best = max(best, 1 + max(dirs.values(), default=0))
return best
pts = [(1, 1), (3, 2), (5, 3), (4, 1), (2, 3), (1, 4)]
print(max_points(pts)) # 4O(n²) pairs. Assumes distinct points; with duplicates, count them separately and add them to every line through that point.
Fibonacci mod m by matrix power
When n is huge (10¹⁸) and the recurrence is linear: square the matrix instead of looping n times.
type Mat = tuple[int, int, int, int] # [[a, b], [c, d]]
def mat_mul(x: Mat, y: Mat, m: int) -> Mat:
a, b, c, d = x
e, f, g, h = y
return (
(a * e + b * g) % m,
(a * f + b * h) % m,
(c * e + d * g) % m,
(c * f + d * h) % m,
)
def fib(n: int, m: int = 10**9 + 7) -> int:
result: Mat = (1, 0, 0, 1) # identity
base: Mat = (1, 1, 1, 0)
while n: # square-and-multiply, like pow
if n & 1:
result = mat_mul(result, base, m)
base = mat_mul(base, base, m)
n >>= 1
return result[1] # base**n = [[F(n+1), F(n)], ...]
print([fib(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
print(fib(10**18)) # 209783453O(log n) matrix products. Any recurrence f(n) = c1·f(n−1) + … + ck·f(n−k) works with a k × k
matrix.
References
- Python docs: Numeric types (opens in a new tab): operators,
//and%rules,round - Python docs: Bitwise operations on integer types (opens in a new tab) and additional methods on integers (opens in a new tab):
bit_length,bit_count,to_bytes - Python docs: Integer string conversion length limitation (opens in a new tab): the 4300-digit limit and what it covers
- Python docs: math (opens in a new tab):
isqrt,gcd,comb,isclose,fsum - Python docs: Built-in functions (opens in a new tab):
int,pow,round,divmod,sum - Python docs: Floating-point arithmetic: issues and limitations (opens in a new tab): why
0.1 + 0.2 != 0.3 - Python docs: fractions (opens in a new tab) and decimal (opens in a new tab): exact arithmetic
- Python docs: Operator precedence (opens in a new tab): why
-2 ** 2 == -4andn & 1 == 0needs no parentheses - cp-algorithms: Sieve of Eratosthenes (opens in a new tab), binary exponentiation (opens in a new tab) and modular inverse (opens in a new tab)