math.py
View source code here on GitHub!
Includes
- python.src.lib.math.from_digits(digs: Iterable[int], base: int = 10) int
Reconstruct a number from a series of digits.
Runs in \(O(n)\) operations, where \(n\) is the number of digits. This means that it will take \(O(\log(m))\) where \(m\) is the original number.
- python.src.lib.math.lattice_paths(height: int, width: int) int
Enumerate the paths from one corner of a Manhattan grid to its opposite.
- python.src.lib.math.mul_inv(a: int, b: int) int
Multiplicative inverse for modulo numbers
Runs in \(O(\log(\min(a, b)))\) time. Given that this is typically used in the context of modulus math, we can usually assume \(a < b\), simplifying this to \(O(\log(a))\) time.
- python.src.lib.math.n_choose_r(n: int, r: int) int
Enumerate the number of ways to pick r elements from a collection of size n.
Runs in \(O(n)\) multiplications. Because of how Python works, numbers less than \(2^{64}\) will multiply in constant time. Larger numbers, however, will multiply in \(O(n^{1.585})\), giving an overall time complexity of \(O(n^{2.585})\).
1from math import factorial, sqrt
2from typing import Iterable
3
4
5def from_digits(digs: Iterable[int], base: int = 10) -> int:
6 """Reconstruct a number from a series of digits.
7
8 Runs in :math:`O(n)` operations, where :math:`n` is the number of digits. This means that it will take
9 :math:`O(\\log(m))` where :math:`m` is the original number."""
10 ret: int = 0
11 for dig in digs:
12 ret = ret * base + dig
13 return ret
14
15
16def lattice_paths(height: int, width: int) -> int:
17 """Enumerate the paths from one corner of a Manhattan grid to its opposite."""
18 return n_choose_r(height + width, height)
19
20
21def mul_inv(a: int, b: int) -> int:
22 """Multiplicative inverse for modulo numbers
23
24 Runs in :math:`O(\\log(\\min(a, b)))` time. Given that this is typically used in the context of modulus math, we
25 can usually assume :math:`a < b`, simplifying this to :math:`O(\\log(a))` time."""
26 if b == 1:
27 return 1
28 b0: int = b
29 x0: int = 0
30 x1: int = 1
31 while a > 1:
32 q: int = a // b
33 a, b = b, a % b
34 x0, x1 = x1 - q * x0, x0
35 if x1 < 0:
36 return x1 + b0
37 return x1
38
39
40def n_choose_r(n: int, r: int) -> int:
41 """Enumerate the number of ways to pick r elements from a collection of size n.
42
43 Runs in :math:`O(n)` multiplications. Because of how Python works, numbers less than :math:`2^{64}` will multiply
44 in constant time. Larger numbers, however, will multiply in :math:`O(n^{1.585})`, giving an overall time complexity
45 of :math:`O(n^{2.585})`."""
46 return factorial(n) // factorial(r) // factorial(n - r)
47
48
49def triangle(n: int) -> int:
50 """Generates the sum of 1 thru n in O(1)."""
51 return n * (n + 1) // 2
52
53
54def quadratic(n: int, a: int, b: int) -> int:
55 return (n + a) * n + b
56
57
58def pentagonal(n: int) -> int:
59 return n * (3 * n - 1) // 2
60
61
62def is_pentagonal(x: int) -> int:
63 root = sqrt(24 * x + 1)
64 if not root.is_integer():
65 return False
66 iroot = int(root)
67 if (1 + iroot) % 6:
68 return False
69 return (1 + iroot) // 6
70
71
72def hexagonal(n: int) -> int:
73 return n * (2 * n - 1)