Algebra
Fundamentals
Binary Exponentiation
Factoring Exponentiation
Euclidean algorithm for computing the greatest common divisor
Extended Euclidean Algorithm
Linear Diophantine Equations
Fibonacci Numbers
Math behind why greedy works
Among all resolution with the minimum number of Fibonacci numbers, we are to find the lexicographically largest one.
Facts about shortest sequence:
- uses each Fibonacci number at most once
fibo[i] * 2 = fibo[i - 2] + fibo[i + 1]
- never uses two consecutive Fibonacci numbers
Then: If no dup, no adjacent, we must take the biggest.
fibo[0] + fibo[2] + fibo[4] + ... + fibo[2n] = fibo[2n + 1] - 1
fibo[1] + fibo[3] + fibo[5] + .... + fibo[2n-1] = fibo[2n] - 1
Prime numbers
Sieve of Eratosthenes
sieve = [False,False]+[True]*(n-1)
for i in range(2,int(r**0.5)+2):
for j in range(i*i,n+1,i):
sieve[j] = False
primes = [i for i,p in enumerate(primes) if p]
# O(nlog(logn))
- find all primes less or equal to n
primes = []
for d in range(2,n+1):
for p in primes:
if d%p == 0: break
else:
primes.append(d)
Linear Sieve
Primality tests
Integer factorization
- trial division
'smallest divisor should be prime'
# O(sqrt(n))
def factorization(n):
res = []
for d in range(2,int(n**0.5)+1):
while n%d == 0:
res.append(d)
n //= d
return res
- simple sieve of eratosthenes + dp
@cache
def factorization(num):
if num == 1: return [1]
if sieve[num]: return [num]
for p in range(2,num+1):
if sieve[p] and num%p==0: return [p]+factorization(num//p)
sieve = [False,False]+[True]*(n-1)
for i in range(2,int(n**0.5)+2):
for j in range(i*i,n+1,i):
sieve[j] = False
iterative implementation:
sieve = [False,False]+[True]*(n-1)
for i in range(2,int(n**0.5)+2):
for j in range(i*i,n+1,i):
sieve[j] = False
primes = [p for p in range(len(sieve)) if sieve[p]]
res = []
for d in primes:
if d * d > n: break
while n % d == 0:
res.append(d)
n //= d
if n > 1:
res.append(n) # n-prime
- Fermat's factorization method
- Pollard's method
- Pollard's rho algorithm
- Floyd's cycle-finding algorithm
- Brent's algorithm
Number-theoretic functions
Euler's totient function
Number of divisors / sum of divisors
Lagrange's four-square theorem
Any number can be represented by at most 4 squares. Check proof in wikipedia.
Modular arithmetic
Modular Inverse
A modular multiplicative inverse of an integer is an integer such that is congruent to modular some modulus . To write it in a formal way: we want to find an integer so that
We will also denote simply with .
It can be proven that the modular inverse exists if and only if and are relatively prime (i.e. ).
Finding the Modular Inverse using Binary Exponentiation
pow(a,m-2,m) # m must be prime/Fermat's little theorem
- For an arbitrary (but coprime) modulus :
- For a prime modulus :
Finding the Modular Inverse using Extended Euclidean algorithm
Use the identity eqaution (linear diophantine equation in two variables)
Take modulu m both sides and you get the result.
The function below solves , for given and .
def extended_euclidean(a, b):
if b == 0:
return 1, 0, a
else:
x, y, gcd = extended_euclidean(b, a % b)
return y, x - (a // b) * y, gcd
Linear Congruence Equation
Chinese Remainder Theorem
Factorial modulo p
Discrete Log
Primitive Root
Discrete Root
Montgomery Multiplication
#Number (Private) systems
Balanced Ternary
Gray code
Note how graycode construction adds 0-s and 1-s(reverses these) in the previous solution
class Solution:
def circularPermutation(self, n: int, s: int) -> List[int]:
graycode = [i ^ (i >> 1) for i in range(1<<n)]
i = graycode.index(s)
print(graycode)
return graycode[i:]+graycode[:i]
class Solution:
def circularPermutation(self, n: int, s: int) -> List[int]:
return [s ^ i ^ (i >> 1) for i in range(1<<n)]
class Solution:
def circularPermutation(self, n: int, start: int) -> List[int]:
# my construct of consecutive bits add 0 and 1 to previous solution
# this is the same as gray code...
def dp(i):
if i == 1: return ['0','1']
res,rev = [],[]
for code in dp(i-1):
res.append('0'+code)
rev.append('1'+code)
return res + rev[::-1]
res = dp(n)
res = [int(b,2) for b in res]
i = res.index(start)
return res[i:] + res[:i]
It's not super obvious why XOR and XOR would differ by 1 bit.
XOR is commulative. we need to prove that XOR XOR XOR is a power of two.
XOR and XOR XOR and XOR XOR
Finding inverse Gray code
Given Gray code , restore the original number , i.e. , given find
We will move from the most significant bits to the least significant ones (the least significant bit has index 1 and the most significant bit has index ). The relation between the bits of number and the bits of number :
in binary presentation
Rewrite XOR to get:
def gray_to_dec(g):
res = 0
while g:
res ^= g
g >>= 1
return res
Miscellaneous
Enumerating submasks of a bitmask
Given a bitmask , you want to efficiently iterate through all of its submasks, that is, masks in which only bits that were included in mask are set.
Consider the implementation of this algorithm, based on tricks with bit operations:
while sub:
print(sub)
sub = (sub-1) & m
The above code visits all submasks of , without repetition, and in descending order. Suppose we have a current bitmask , and we want to move on to the next bitmask. By subtracting from the mask one unit, we will remove the rightmost set bit and all bits to the right of it will become 1. Then we remove all the "extra" one bits that are not included in the mask and therefore can't be a part of a submask.
Iterating through all masks with their submasks. Complexity
In many problems, especially those that use bitmask dynamic programming, you want to iterate through all bitmasks and for each mask, iterate through all of its submasks:
for (int m=0; m<(1<<n); ++m):
get_all_submasks(m)
Let's prove that the inner loop will execute a total of iterations.
First proof: Consider the -th bit. There are exactly three options for it:
- it is not included in the mask (and therefore not included in submask ),
- it is included in , but not included in , or
- it is included in both and .
As there are a total of bits, there will be different combinations.
Second proof: Note that if mask has enabled bits, then it will have submasks. As we have a total of masks with enabled bits (see binomial coefficients), then the total number of combinations for all masks will be:
To calculate this number, note that the sum above is equal to the expansion of using the binomial theorem. Therefore, we have combinations, as we wanted to prove.