├── README.md ├── Recursion-limits.py ├── Mathematics ├── Number-theory │ ├── Legendre-symbol.py │ ├── Sieving-methods │ │ ├── Sieve-of-Eratosthenes.py │ │ ├── Divisor-functions.py │ │ ├── Mobius-function.py │ │ ├── Prime-counting-function.py │ │ ├── Euler-totient-function.py │ │ └── Totient-summatory-function.py │ ├── Euler-totient-function.py │ ├── Stirling-numbers-first-kind.py │ ├── Catalan-numbers.py │ ├── Euclidean-algorithms.py │ ├── Bell-numbers.py │ ├── Bernoulli-numbers.py │ ├── Modular-square-root.py │ ├── Stirling-numbers-second-kind.py │ ├── Chinese-remainder-theorem.py │ └── Binomial-numbers.py ├── Precise-square-root.py └── Continued-fractions.py ├── Array-algorithms ├── Maximum-xor-of-two-numbers.py ├── Kadane-algorithm.py ├── Longest-increasing-subsequence.py ├── Counting-sort.py ├── Longest-common-substring.py ├── Longest-common-subequence.py ├── Z-algorithm.py └── K-th-element.py ├── Data-structures ├── Trie-string.py ├── Trie-binary.py ├── Disjoint-set.py ├── Heap-custom-comparator.py ├── Segment-tree-lazy.py ├── Segment-tree.py └── Treap.py ├── Miscellaneous ├── Binary-search.py ├── Ternary-search.py └── Iterated-sequence-cycle.py └── Graph-algorithms └── Dijkstra.py /README.md: -------------------------------------------------------------------------------- 1 | # Competitive programming in Python 3 2 | -------------------------------------------------------------------------------- /Recursion-limits.py: -------------------------------------------------------------------------------- 1 | import sys; sys.setrecursionlimit(300000) 2 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Legendre-symbol.py: -------------------------------------------------------------------------------- 1 | ''' Legendre symbol indicates whether a is a quadratic residue modulo p ''' 2 | 3 | 4 | 5 | ######################################################################### 6 | 7 | def legendre_symbol(a, p): 8 | ls = pow(a, (p - 1)//2, p) 9 | return -1 if ls == p - 1 else ls 10 | 11 | ######################################################################### 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ''' Verification test ''' 20 | if __name__ == "__main__": 21 | assert [legendre_symbol(a, 23) for a in range(1, 31)] == [int(x) for x in \ 22 | "1 1 1 1 -1 1 -1 1 1 -1 -1 1 1 -1 -1 1 -1 1 -1 -1 -1 -1 0 1 1 1 1 -1 1 -1".split()] 23 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Sieving-methods/Sieve-of-Eratosthenes.py: -------------------------------------------------------------------------------- 1 | def fast_prime_sieve(n): 2 | """ 3 | Input n>=6, Returns a list of primes, 2 <= p <= n 4 | Taken from: https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/ 5 | """ 6 | n += 1 7 | n, correction = n-n%6+6, 2-(n%6>1) 8 | sieve = [True] * (n//3) 9 | for i in range(1,int(n**0.5)//3+1): 10 | if sieve[i]: 11 | k=3*i+1|1 12 | sieve[ k*k//3 ::2*k] = [False] * ((n//6-k*k//6-1)//k+1) 13 | sieve[k*(k-2*(i&1)+4)//3::2*k] = [False] * ((n//6-k*(k-2*(i&1)+4)//6-1)//k+1) 14 | return [2,3] + [3*i+1|1 for i in range(1,n//3-correction) if sieve[i]] 15 | -------------------------------------------------------------------------------- /Array-algorithms/Maximum-xor-of-two-numbers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Finds maximum XOR of two numbers in an array. 3 | ''' 4 | 5 | 6 | 7 | ######################################################################### 8 | 9 | def maximum_xor(nums): 10 | answer = 0 11 | for i in range(31, -1, -1): 12 | answer <<= 1 13 | prefixes = {num >> i for num in nums} 14 | answer += any(answer^1^p in prefixes for p in prefixes) 15 | return answer 16 | 17 | 18 | ######################################################################### 19 | 20 | 21 | 22 | 23 | 24 | ''' Verification test ''' 25 | if __name__ == "__main__": 26 | # Passed https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/ 27 | pass 28 | -------------------------------------------------------------------------------- /Array-algorithms/Kadane-algorithm.py: -------------------------------------------------------------------------------- 1 | ''' Finds the maximum subarray sum of a. ''' 2 | 3 | 4 | ######################################################################### 5 | 6 | def max_subarray(a): 7 | max_so_far = max_ending_here = 0 8 | for el in a: 9 | # in case a speed-up is required, change the below max/min to if-else statements. 10 | max_ending_here = max(0, max_ending_here + el) 11 | max_so_far = max(max_so_far, max_ending_here) 12 | return max_so_far 13 | 14 | 15 | ######################################################################### 16 | 17 | 18 | 19 | 20 | ''' Verification test ''' 21 | if __name__ == "__main__": 22 | for arr in [[-1,2,-3,4,-5,5], [-1,-2,-3], [], [0], [2, -2, 1, 2, -1, 3, -1, -1, -1]]: 23 | print('{:s} has maximum subarray sum {:d}.'.format(str(arr), max_subarray(arr))) 24 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Euler-totient-function.py: -------------------------------------------------------------------------------- 1 | ''' Euler"s totient function for a single number ''' 2 | 3 | 4 | ######################################################################### 5 | 6 | from functools import lru_cache 7 | 8 | def factorize(n): 9 | factorization = set() 10 | while n > 1: 11 | for i in range(2, n + 1): 12 | if n % i == 0: 13 | n //= i 14 | factorization.add(i) 15 | break 16 | return factorization 17 | 18 | @lru_cache(maxsize=None) 19 | def phi(n): 20 | totient = n 21 | for p in factorize(n): 22 | totient -= totient//p 23 | return totient 24 | 25 | ######################################################################### 26 | 27 | 28 | 29 | ''' Verification test ''' 30 | if __name__ == "__main__": 31 | assert phi(12312) == 3888 32 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Stirling-numbers-first-kind.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Stirling number of first kind s(n, k) is the number of permutations of length n having exactly k cycles. 3 | ''' 4 | 5 | 6 | 7 | 8 | ######################################################################### 9 | 10 | # Works modulo MOD 11 | def Stirling(n, k, MOD): 12 | if k > n: 13 | return 0 14 | s = [[0 for _ in range(k+1)] for _ in range(n+1)] 15 | s[0][0] = 1 16 | for i in range(1, n+1): 17 | for j in range(1, k+1): 18 | s[i][j] = ((i-1)*s[i-1][j] + s[i-1][j-1]) % MOD 19 | return s[n][k] 20 | 21 | ######################################################################### 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ''' Verification test ''' 30 | if __name__ == "__main__": 31 | MOD = 10**9+7 32 | assert Stirling(9, 5, MOD) == 22449 33 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Catalan-numbers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Calculates Catalan numbers (2n choose n) / (n+1) 3 | ''' 4 | 5 | 6 | 7 | 8 | ######################################################################### 9 | 10 | def Catalan(n, MOD): 11 | # Assumes MOD prime 12 | f = 1 13 | for i in range(1, n+1): 14 | f = f*i % MOD 15 | res = pow(f, MOD-2, MOD) * pow(f*(n+1), MOD-2, MOD) % MOD 16 | for i in range(n+1, 2*n+1): 17 | f = f*i % MOD 18 | res *= f 19 | return res % MOD 20 | 21 | ######################################################################### 22 | 23 | 24 | 25 | 26 | 27 | 28 | ''' Verification test ''' 29 | if __name__ == "__main__": 30 | MOD = 10**9 + 7 31 | arr = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700] 32 | assert all(x%MOD == y%MOD for x, y in zip(arr, [Catalan(n, MOD) for n in range(len(arr))])) 33 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Euclidean-algorithms.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Euclidean algorithms. 3 | ''' 4 | 5 | 6 | ######################################################################### 7 | 8 | def egcd(a, b): 9 | ''' Extended Euclidian algorithm. ''' 10 | if a == 0: 11 | return (b, 0, 1) 12 | else: 13 | g, y, x = egcd(b % a, a) 14 | # x * a + y * b = g 15 | return (g, x - (b // a) * y, y) 16 | 17 | _gcd = lambda a, b: a+b if a==0 or b==0 else gcd(b, a % b) 18 | 19 | def gcd(a, b): 20 | if a==0 or b == 0: 21 | return a+b 22 | return gcd(b, a % b) 23 | 24 | 25 | ######################################################################### 26 | ''' Verification test ''' 27 | from random import randint 28 | import fractions 29 | if __name__ == "__main__": 30 | for a, b in [(randint(1, 10**9), (randint(1, 10**9))) for _ in range(20)]: 31 | assert fractions.gcd(a, b) == gcd(a, b) == _gcd(a, b) == egcd(a, b)[0] 32 | -------------------------------------------------------------------------------- /Mathematics/Precise-square-root.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Calculates square root of a non-square number n up to arbitrary precision. 3 | ''' 4 | 5 | 6 | 7 | 8 | ######################################################################### 9 | 10 | def precise_square_root(n, precision=100): 11 | ''' . ''' 12 | a, b = 5 * n, 5 13 | while len(str(b)) < precision + 5: 14 | if a >= b: 15 | a -= b 16 | b += 10 17 | else: 18 | a *= 100 19 | b = 100 * (b // 10) + 5 20 | sb = str(b) 21 | int_len = len(str(int(n**.5))) 22 | return (sb[:int_len] + '.' + sb[int_len:])[:precision] 23 | 24 | ######################################################################### 25 | 26 | 27 | 28 | 29 | 30 | ''' Verification test ''' 31 | if __name__ == "__main__": 32 | for n in [128937, 16]: 33 | print(' Number {:d}'.format(n)) 34 | print(' Precise square root: {:s}'.format(precise_square_root(n, 30))) 35 | print(' Float square root : {:.19f}'.format(n ** 0.5)) 36 | print() 37 | -------------------------------------------------------------------------------- /Array-algorithms/Longest-increasing-subsequence.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Finds longest increasing subsequence in O(n log n) time. 3 | ''' 4 | 5 | 6 | ######################################################################### 7 | 8 | def lis(a): 9 | p, m, l = [0]*len(a), [0]*(len(a) + 1), 0 10 | for i in range(len(a)): 11 | lo, hi = 1, l 12 | while lo <= hi: 13 | mid = (lo + hi)//2 14 | if a[m[mid]] < a[i]: 15 | lo = mid+1 16 | else: 17 | hi = mid-1 18 | p[i], m[lo] = m[lo-1], i 19 | if lo > l: 20 | l = lo 21 | s, k = [], m[l] 22 | for i in range(l-1, -1, -1): 23 | s.append(a[k]) 24 | k = p[k] 25 | return s[::-1] 26 | 27 | ######################################################################### 28 | 29 | 30 | 31 | 32 | 33 | 34 | ''' Verification test ''' 35 | if __name__ == "__main__": 36 | for a in [[-1,2,-3,4,-5,5], [-1,-2,-3], [], [0], [2, -2, 1, 2, -1, 3, -1, -1, -1]]: 37 | print('{:s} has longest increasing sequence {:s}.'.format(str(a), str(lis(a)))) 38 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Bell-numbers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Bell number B_n is the number of different ways to partition (possibly empty) a set that has exactly n elements. 3 | ''' 4 | 5 | 6 | 7 | 8 | ######################################################################### 9 | # DP approach 10 | 11 | def Bell(n, MOD): 12 | b = [[0 for i in range(n+1)] for j in range(n+1)] 13 | b[0][0] = 1 14 | for i in range(1, n+1): 15 | b[i][0] = b[i-1][i-1] 16 | for j in range(1, i+1): 17 | b[i][j] = (b[i-1][j-1] + b[i][j-1]) % MOD 18 | return b[n][0] 19 | 20 | ######################################################################### 21 | 22 | 23 | 24 | 25 | 26 | ''' Verification test ''' 27 | if __name__ == "__main__": 28 | MOD = 10**9 + 7 29 | arr = [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975, 678570, 4213597, 27644437, 190899322, 1382958545, 10480142147, 82864869804, 682076806159, 5832742205057, 51724158235372, 474869816156751, 4506715738447323, 44152005855084346] 30 | assert all(x%MOD == y%MOD for x, y in zip(arr, [Bell(n, MOD) for n in range(len(arr))])) 31 | -------------------------------------------------------------------------------- /Data-structures/Trie-string.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Trie data-structure for strings. 3 | ''' 4 | 5 | 6 | ######################################################################### 7 | 8 | Trie = {} 9 | 10 | def add(word): 11 | temp = Trie 12 | for ch in word: 13 | if ch not in temp: 14 | temp[ch] = [0, {}] 15 | temp[ch][0] += 1 16 | temp = temp[ch][1] 17 | 18 | def remove(word): 19 | # Assumes word is in the Trie 20 | temp = Trie 21 | for ch in word: 22 | temp[ch][0] -= 1 23 | temp = temp[ch][1] 24 | 25 | def count_prefix(prefix): 26 | # Counts number of inserted words with a given prefix 27 | temp = Trie 28 | n = 0 29 | for c in prefix: 30 | if c not in temp: 31 | return 0 32 | n = temp[c][0] 33 | if n == 0: 34 | break 35 | temp = temp[c][1] 36 | return n 37 | ######################################################################### 38 | 39 | 40 | 41 | 42 | 43 | 44 | ''' Verification test ''' 45 | if __name__ == "__main__": 46 | # Passed https://www.hackerrank.com/challenges/contacts/problem 47 | pass 48 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Bernoulli-numbers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Computes the Bernoulli numbers | https://en.wikipedia.org/wiki/Bernoulli_number 3 | 4 | Note that the second term is subject to a sign convention B_1 = \pm 1/2 5 | ''' 6 | 7 | ######################################################################### 8 | from fractions import Fraction 9 | 10 | def bernoulli(): 11 | ''' Generator of Bernoulli numbers. ''' 12 | ber, m = [], 0 13 | while 1: 14 | ber.append(Fraction(1, m+1)) 15 | for j in range(m, 0, -1): 16 | ber[j-1] = j*(ber[j-1] - ber[j]) 17 | yield ber[0] 18 | m += 1 19 | ########################################################################## 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ''' Verification test ''' 28 | if __name__ == "__main__": 29 | bernoullis = [inum for inum in zip(range(61), bernoulli())] 30 | 31 | for n, b_n in bernoullis: 32 | print('B[{:d}] = {:s}'.format(n, str(b_n))) 33 | 34 | ''' 35 | Approximate runtime (Computing B[n]): 36 | _______________________________________________ 37 | n time 38 | 100 0.05s 39 | 200 0.28s 40 | 300 0.84s 41 | 500 4.00s 42 | ''' 43 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Sieving-methods/Divisor-functions.py: -------------------------------------------------------------------------------- 1 | ''' Sieve for the divisor functions. ''' 2 | 3 | ######################################################################### 4 | 5 | def num_of_divisors(n): 6 | nd = [0] * (n+1) 7 | for i in range(1, n+1): 8 | for j in range(i, n+1, i): 9 | nd[j] += 1 10 | return nd 11 | 12 | 13 | def sum_of_divisors(n): 14 | sd = [0] * (n+1) 15 | for i in range(1, n+1): 16 | for j in range(i, n+1, i): 17 | sd[j] += i 18 | return sd 19 | 20 | ######################################################################### 21 | 22 | 23 | 24 | 25 | 26 | ''' Verification test ''' 27 | if __name__ == '__main__': 28 | from sympy import divisors 29 | from random import randint 30 | for _ in range(20): 31 | n = randint(1, 10**4) 32 | d = divisors(n) 33 | assert num_of_divisors(n)[-1] == len(d) 34 | assert sum_of_divisors(n)[-1] == sum(d) 35 | 36 | ''' 37 | Approximate runtime (Computing num_of_divisors(n), sum_of_divisors(n)): 38 | _______________________________________________ 39 | n time 40 | 100 1e-4s 41 | 10**4 0.004s 42 | 10**6 0.08s 43 | 10**8 22s 44 | ''' 45 | -------------------------------------------------------------------------------- /Miscellaneous/Binary-search.py: -------------------------------------------------------------------------------- 1 | ''' Selection of binary search algorithm variants. ''' 2 | 3 | 4 | ######################################################################### 5 | 6 | 7 | from bisect import bisect_left 8 | 9 | def binary_search(a, x): 10 | ''' Returns index of element x in sorted array a if present, otherwise returns -1 ''' 11 | pos = bisect_left(a, x) 12 | return (pos if pos != len(a) and a[pos] == x else -1) 13 | 14 | def continuous_binary_search(f, value, l, r, eps=1e-9): 15 | ''' Finds f^(-1)(value) where f is an increasing function ''' 16 | while abs(f((l+r)/2) - value) > eps: 17 | mid = (r + l)/2 18 | if f(mid) > value: 19 | r = mid 20 | else: 21 | l = mid 22 | return (l + r)/2 23 | 24 | 25 | 26 | ######################################################################### 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ''' Verification test ''' 38 | if __name__ == '__main__': 39 | a = [1, 1, 2, 2, 3, 4, 6, 7, 8, 9] 40 | for qry, ans in [(-1, -1), (0, -1), (1, 0), (2, 2), (3, 4), (5, -1), (9, 9), (10, -1)]: 41 | assert binary_search(a, qry) == ans 42 | 43 | # finding the root of a cubic function 44 | f = lambda x: (x - 500)**3 + 42 45 | root = continuous_binary_search(f, 0, -10**6, 10**6) 46 | print('Root =', root, 'f(root) =', f(root)) 47 | -------------------------------------------------------------------------------- /Array-algorithms/Counting-sort.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Count sort. Outperforms the default sorting routine when the range of values is reasonably bounded. 3 | 4 | ''' 5 | 6 | ######################################################################### 7 | 8 | def count_sort(a): 9 | mn, mx = float('inf'), -float('inf') 10 | for x in a: 11 | if x < mn: mn = x 12 | if x > mx: mx = x 13 | counter = [0 for _ in range(mx - mn + 1)] 14 | for x in a: 15 | counter[x - mn] += 1 16 | j = 0 17 | for i in range(mx - mn + 1): 18 | a[j:j+counter[i]] = [i + mn]*counter[i] 19 | j += counter[i] 20 | 21 | 22 | ######################################################################### 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ''' Verification test ''' 32 | if __name__ == "__main__": 33 | from random import randint 34 | from time import clock 35 | a = [randint(10**4, 10**4 + 10**5) for _ in range(10**6)] 36 | aa = list(a) 37 | start = clock(); count_sort(a); print('Count sort:', clock() - start) 38 | start = clock(); aa.sort(); print('Default sort:', clock() - start) 39 | assert a == aa 40 | 41 | a = [randint(-10**6, 10**6) for _ in range(10**7)] 42 | aa = list(a) 43 | start = clock(); count_sort(a); print('Count sort:', clock() - start) 44 | start = clock(); aa.sort(); print('Default sort:', clock() - start) 45 | assert a == aa 46 | -------------------------------------------------------------------------------- /Miscellaneous/Ternary-search.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Selection of ternary search algorithm variants. 3 | 4 | Ternary search finds maximum of unimodal lists/functions in log time. 5 | ''' 6 | 7 | 8 | ######################################################################### 9 | 10 | def ternary_search(a): 11 | # Finds maximum of unimodal array. For minimum, reverse comparison 12 | l, r = 0, len(a) 13 | while r > l + 2: 14 | lmid, rmid = l + (r - l) // 3, r - (r - l) // 3 15 | if a[lmid] < a[rmid]: 16 | l = lmid 17 | else: 18 | r = rmid 19 | return max([(a[i], i) for i in range(l, r+1)])[1] 20 | 21 | 22 | def continuous_ternary_search(f, l, r, eps=1e-9): 23 | # Finds maximum. For minimum, reverse comparison 24 | while r - l > eps: 25 | lmid, rmid = l + (r - l) / 3, r - (r - l) / 3 26 | if f(lmid) < f(rmid): 27 | l = lmid 28 | else: 29 | r = rmid 30 | return (l + r)/2 31 | 32 | 33 | ######################################################################### 34 | 35 | 36 | 37 | 38 | ''' Verification test ''' 39 | if __name__ == '__main__': 40 | # finding maximum of a quadtratic function 41 | f = lambda x: -(x - 42)**2 + 65 42 | v = continuous_ternary_search(f, -10**6, 10**6) 43 | print('Maximum of function is at', v, ' with value ', f(v)) 44 | 45 | a = [1, 1, 2, 2, 3, 4, 6, 7, 8, 122, 8, 6, 6, 5, 3, 2, 1, 1] + [0]*10**5 46 | assert ternary_search(a) == 9 47 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Sieving-methods/Mobius-function.py: -------------------------------------------------------------------------------- 1 | ''' Sieve for the Mobius function. ''' 2 | 3 | ######################################################################### 4 | 5 | 6 | def fast_prime_sieve(n): 7 | """ 8 | Input n>=6, Returns a list of primes, 2 <= p <= n 9 | Taken from: https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/ 10 | """ 11 | n += 1 12 | n, correction = n-n%6+6, 2-(n%6>1) 13 | sieve = [True] * (n//3) 14 | for i in range(1,int(n**0.5)//3+1): 15 | if sieve[i]: 16 | k=3*i+1|1 17 | sieve[ k*k//3 ::2*k] = [False] * ((n//6-k*k//6-1)//k+1) 18 | sieve[k*(k-2*(i&1)+4)//3::2*k] = [False] * ((n//6-k*(k-2*(i&1)+4)//6-1)//k+1) 19 | return [2,3] + [3*i+1|1 for i in range(1,n//3-correction) if sieve[i]] 20 | 21 | 22 | def mobius(n, primes): 23 | mu = [1 for _ in range(n+1)] 24 | for p in primes: 25 | for j in range(p, n+1, p): 26 | mu[j] *= -1 27 | for j in range(p*p, n+1, p*p): 28 | mu[j] = 0 29 | return mu 30 | 31 | ######################################################################### 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ''' 41 | Approximate runtime (Computing mobius(n), including primes): 42 | _______________________________________________ 43 | n time 44 | 100 1e-4s 45 | 10**4 0.008s 46 | 10**6 0.08s 47 | 10**8 11s 48 | ''' 49 | -------------------------------------------------------------------------------- /Array-algorithms/Longest-common-substring.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Finds longest common substring of two strings. 3 | 4 | The DP approach takes O(|a||b|) time. 5 | ''' 6 | 7 | 8 | ######################################################################### 9 | 10 | 11 | # DP approach 12 | def lcss(a, b): 13 | max_len, max_substring = 0, [] 14 | a += [max(a+b)+1] 15 | b += [a[-1]+1] 16 | suffixes = sorted([a[i:] for i in range(len(a)-1)] + [b[i:] for i in range(len(b)-1)]) 17 | previous = suffixes[0] 18 | for current in suffixes[1:]: 19 | if previous[-1] != current[-1]: 20 | for i, c in enumerate(previous): 21 | if c != current[i]: 22 | break 23 | else: 24 | i = len(previous) 25 | if i > max_len: 26 | max_len = i 27 | max_substring = previous[:i] 28 | previous = current 29 | return max_substring 30 | 31 | 32 | 33 | ######################################################################### 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ''' Verification test ''' 42 | if __name__ == "__main__": 43 | for a in [[-1,-2,-3,4,-5,5], [0,-1,-2,-3], [], [0], [2, -2, 1, 2, -1, 3, -1, -1, -1]]: 44 | for b in [[-1,-2,-3,4,-5,5], [1, -1,-2,-3], [], [0], [2, -2, 1, 2, -1, 3, -1, -1, -1]]: 45 | if a<=b: 46 | print('Longest common substring of:\n {:s}\n {:s}'.format(str(a), str(b))) 47 | print('is: {:s}\n'.format(str(lcss(a,b)))) 48 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Modular-square-root.py: -------------------------------------------------------------------------------- 1 | ''' Modular square root ''' 2 | 3 | 4 | ######################################################################### 5 | 6 | def legendre_symbol(a, p): 7 | ls = pow(a, (p - 1) // 2, p) 8 | return -1 if ls == p - 1 else ls 9 | 10 | def modular_sqrt(a, p): 11 | if legendre_symbol(a, p) != 1: 12 | return 0 13 | elif a == 0: 14 | return 0 15 | elif p == 2: 16 | return 0 17 | elif p % 4 == 3: 18 | return pow(a, (p + 1) // 4, p) 19 | s = p - 1 20 | e = 0 21 | while s % 2 == 0: 22 | s //= 2 23 | e += 1 24 | n = 2 25 | while legendre_symbol(n, p) != -1: 26 | n += 1 27 | x = pow(a, (s + 1) // 2, p) 28 | b = pow(a, s, p) 29 | g = pow(n, s, p) 30 | r = e 31 | 32 | while True: 33 | t = b 34 | m = 0 35 | for m in range(r): 36 | if t == 1: 37 | break 38 | t = pow(t, 2, p) 39 | 40 | if m == 0: 41 | return x 42 | gs = pow(g, 2 ** (r - m - 1), p) 43 | g = (gs * gs) % p 44 | x = (x * gs) % p 45 | b = (b * g) % p 46 | r = m 47 | 48 | ######################################################################### 49 | 50 | 51 | 52 | 53 | ''' Verification test ''' 54 | if __name__ == "__main__": 55 | for p in [17, 23, 53]: 56 | for i in range(1, p): 57 | r = modular_sqrt(i, p) 58 | if r: 59 | assert r*r%p == i 60 | -------------------------------------------------------------------------------- /Miscellaneous/Iterated-sequence-cycle.py: -------------------------------------------------------------------------------- 1 | ''' Detects cycles in a given iterator. ''' 2 | 3 | 4 | ######################################################################### 5 | 6 | def cycle_length(f, x0, nmax=None): 7 | ''' Given iterator f, returns lambda, mu. 8 | 9 | Lambda is the length of the cycle, mu is the number of iterations before the cycle. 10 | ''' 11 | nmax = int(nmax or 0) 12 | 13 | # main phase: search successive powers of two 14 | power = lam = 1 15 | tortoise, hare, i = x0, f(x0), 0 16 | while tortoise != hare and (not nmax or i < nmax): 17 | i += 1 18 | if power == lam: # time to start a new power of two? 19 | tortoise = hare 20 | power <<= 1 21 | lam = 0 22 | hare = f(hare) 23 | lam += 1 24 | if nmax and i == nmax: 25 | return nmax, None 26 | 27 | # Find the position of the first repetition of length lambda 28 | mu = 0 29 | tortoise = hare = x0 30 | for i in range(lam): 31 | hare = f(hare) 32 | while tortoise != hare: 33 | tortoise = f(tortoise) 34 | hare = f(hare) 35 | mu += 1 36 | return lam, max(mu-1, 0) 37 | 38 | 39 | 40 | ######################################################################### 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ''' Verification test ''' 51 | if __name__ == '__main__': 52 | f = lambda x: x-1 if x >= 10000 else (x+1)%10000 53 | x0 = 10000 + 123 54 | assert cycle_length(f, x0) == (10000, 123) 55 | -------------------------------------------------------------------------------- /Array-algorithms/Longest-common-subequence.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Finds longest common subsequence of two arrays. 3 | ''' 4 | 5 | ######################################################################### 6 | 7 | 8 | 9 | def lcs(a, b): 10 | ''' Finds longest common subsequence of arrays a, b. ''' 11 | lengths = [[0 for j in range(len(b) + 1)] for i in range(len(a) + 1)] 12 | for i, x in enumerate(a): 13 | for j, y in enumerate(b): 14 | if x == y: 15 | lengths[i+1][j+1] = lengths[i][j] + 1 16 | else: 17 | lengths[i+1][j+1] = max(lengths[i+1][j], lengths[i][j+1]) 18 | result = [] 19 | x, y = len(a), len(b) 20 | while x != 0 and y != 0: 21 | if lengths[x][y] == lengths[x-1][y]: 22 | x -= 1 23 | elif lengths[x][y] == lengths[x][y-1]: 24 | y -= 1 25 | else: 26 | result.append(a[x-1]) 27 | x, y = x - 1, y - 1 28 | return result[::-1] 29 | 30 | 31 | ######################################################################### 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ''' Verification test ''' 41 | if __name__ == "__main__": 42 | for a in [[-1,2,-3,4,-5,5], [-1,-2,-3], [], [0], [2, -2, 1, 2, -1, 3, -1, -1, -1]]: 43 | for b in [[-1,2,-3,4,-5,5], [-1,-2,-3], [], [0], [2, -2, 1, 2, -1, 3, -1, -1, -1]]: 44 | if a<=b: 45 | print('Longest common subsequence of:\n {:s}\n {:s}'.format(str(a), str(b))) 46 | print('is: {:s}\n'.format(str(lcs(a,b)))) 47 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Stirling-numbers-second-kind.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Stirling number of second kind S(n, k) is the number of ways to partition a set of n objects into k non-empty subsets. 3 | ''' 4 | 5 | 6 | 7 | 8 | ######################################################################### 9 | 10 | # Works modulo MOD, takes fact (n! % MOD) and factinv (n!^(-1) % MOD) as input 11 | def Stirling(n, k, fact, factinv, MOD): 12 | if k > n: 13 | return 0 14 | result = 0 15 | for j in range(k+1): 16 | result += (-1 if (k-j)&1 else 1) * fact[k] % MOD * factinv[j] * factinv[k-j] % MOD * pow(j, n, MOD) % MOD 17 | result %= MOD 18 | result *= factinv[k] 19 | return result % MOD 20 | 21 | 22 | ######################################################################### 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | def fast_modinv(up_to, M): 31 | ''' Fast modular inverses of 1..up_to modulo M. ''' 32 | modinv = [-1 for _ in range(up_to + 1)] 33 | modinv[1] = 1 34 | for x in range(2, up_to + 1): 35 | modinv[x] = (-(M//x) * modinv[M%x])%M 36 | return modinv 37 | 38 | ''' Verification test ''' 39 | if __name__ == "__main__": 40 | MOD = 10**9+7 41 | maxn = 100 42 | modinv = fast_modinv(maxn, MOD) 43 | fact, factinv = [1], [1] 44 | for i in range(1, maxn): 45 | fact.append(fact[-1]*i % MOD) 46 | factinv.append(factinv[-1]*modinv[i] % MOD) 47 | 48 | assert Stirling(10, 3, fact, factinv, MOD) == 9330 % MOD 49 | assert Stirling(10, 5, fact, factinv, MOD) == 42525 % MOD 50 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Chinese-remainder-theorem.py: -------------------------------------------------------------------------------- 1 | ''' Solves a system of congruences using the C.R.T. 2 | [ x == a_i mod m_i ] 3 | ''' 4 | 5 | 6 | ######################################################################### 7 | 8 | # O(|m| log(max(m) + max(a))) 9 | def chinese_remainder(m, a): 10 | ''' 11 | m - modulos, a - coefficients (arrays) 12 | * m have to be mutually COPRIME * 13 | 14 | ''' 15 | res, prod = 0, 1 16 | for m_i in m: 17 | prod *= m_i 18 | for m_i, a_i in zip(m, a): 19 | p = prod // m_i 20 | res += a_i * modinv(p, m_i) * p 21 | return res % prod 22 | 23 | 24 | 25 | # O(log(a+b)) 26 | def egcd(a, b): 27 | ''' Extended Euclidian algorithm. ''' 28 | if a == 0: 29 | return (b, 0, 1) 30 | else: 31 | g, y, x = egcd(b % a, a) 32 | return (g, x - (b // a) * y, y) 33 | 34 | def modinv(a, m): 35 | ''' Finds modular inverse of a modulo m. ''' 36 | while a < 0: 37 | a += m 38 | g, x, y = egcd(a, m) 39 | if g != 1: 40 | raise Exception('Modular inverse does not exist') 41 | else: 42 | return x % m 43 | 44 | ######################################################################### 45 | 46 | 47 | 48 | 49 | ''' Verification test ''' 50 | if __name__ == '__main__': 51 | m = [11, 16, 21, 25] 52 | a = [6, 13, 9, 19] 53 | x = chinese_remainder(m, a) 54 | for mm, aa in zip(m, a): 55 | assert(x%mm == aa) 56 | 57 | print(' The solution of: ') 58 | for mm, aa in zip(m, a): 59 | print(' x == {:<3d} mod {:d}'.format(aa, mm)) 60 | print(' is x = {:d}'.format(x)) 61 | -------------------------------------------------------------------------------- /Array-algorithms/Z-algorithm.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Computes z-array of string s in O(|s|) time. 3 | Z-array, at index i, stores the length of the longest substring of s 4 | that starts on i-th place and is also a prefix of s 5 | ''' 6 | 7 | ######################################################################### 8 | 9 | def z_array(s): 10 | z = [len(s)] + [0 for _ in range(len(s) - 1)] 11 | 12 | left, right = 0, 0 # stores the box boundaries 13 | for k in range(1, len(s)): 14 | if k > right: # compute naively 15 | cnt = 0 16 | while cnt + k < len(s) and s[cnt] == s[cnt + k]: 17 | cnt += 1 18 | z[k] = cnt 19 | if cnt > 0: 20 | left, right = k, cnt + k - 1 21 | else: # we are inside the box 22 | p = k - left # corresponding index in a prefix 23 | right_part_length = right - k + 1 24 | if z[p] < right_part_length: # use the past results 25 | z[k] = z[p] 26 | else: # compute naively 27 | i = right + 1 28 | while i < len(s) and s[i] == s[i - k]: 29 | i += 1 30 | z[k] = i - k 31 | left, right = k, i - 1 32 | return z 33 | 34 | ######################################################################### 35 | 36 | 37 | 38 | 39 | 40 | ''' Verification test ''' 41 | if __name__ == "__main__": 42 | for s in ['abcxxxabyyy', 'aaaaaa', 'abbbb', 'abcabc', 'abracadabra']: 43 | print('\nString {:s}'.format(s)) 44 | z = z_array(s) 45 | for i in range(len(s)): 46 | print(s[i:].ljust(len(s)) + ' and the string start commonly with: {:s}'.format(s[i:i+z[i]])) 47 | -------------------------------------------------------------------------------- /Data-structures/Trie-binary.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Binary Trie with a xor-maximum operation. 3 | ''' 4 | 5 | ######################################################################### 6 | 7 | max_size = 10**5 8 | num_bits = 32 9 | 10 | units = [(1< k: 23 | return find_kth(arr, k, start, pivot_idx) 24 | else: 25 | return find_kth(arr, k, pivot_idx, end) 26 | 27 | def _partition(arr, start, end, pivot_idx): 28 | pivot = arr[pivot_idx] 29 | arr[end], arr[pivot_idx] = arr[pivot_idx], arr[end] 30 | inc_idx = start 31 | for i in range(start, end): 32 | if arr[i] <= pivot: 33 | arr[inc_idx], arr[i] = arr[i], arr[inc_idx] 34 | inc_idx += 1 35 | arr[end], arr[inc_idx] = arr[inc_idx], arr[end] 36 | return inc_idx 37 | 38 | 39 | ######################################################################### 40 | 41 | 42 | 43 | 44 | 45 | ''' Verification test ''' 46 | if __name__ == "__main__": 47 | from random import shuffle, randint 48 | from time import clock 49 | from heapq import nsmallest 50 | for l in range(1, 7): 51 | arr = list(range(1, 10**l)) 52 | k = randint(1, len(arr)) 53 | shuffle(arr) 54 | 55 | start = clock() 56 | assert k == find_kth(arr, k) 57 | print('find_kth for length 10 ^', l , '; k =', k, clock() - start) 58 | 59 | start = clock() 60 | n = nsmallest(k, arr)[k-1] 61 | assert n == k 62 | print('heapq for length 10 ^', l , ': k =', k, clock() - start) 63 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Binomial-numbers.py: -------------------------------------------------------------------------------- 1 | ''' Computes the Binomial numbers. ''' 2 | 3 | ######################################################################### 4 | 5 | # O(k) 6 | def choose(n, k): 7 | if k < 0 or k > n or n < 0: 8 | return 0 9 | 10 | result = 1 11 | for i in range(k): 12 | result *= n - i 13 | result //= i + 1 14 | return result 15 | 16 | # O(k) 17 | def mod_choose(n, k, M, modinv): 18 | ''' Computes {n choose k} mod M ''' 19 | if k < 0 or k > n or n < 0: 20 | return 0 21 | 22 | result = 1 23 | for i in range(k): 24 | result = result * (n - i) % M 25 | result = result * modinv[i + 1] % M 26 | result %= M 27 | return result 28 | 29 | # O(max_n**2) 30 | def mod_choose_dp(max_n, M): 31 | ''' Computes all {n choose k} mod M for 0 <= n <= max_n, using dynamic programming ''' 32 | ch = [[0 for _ in range(max_n+1)] for _ in range(max_n+1)] 33 | ch[0][0] = 1 34 | for n in range(max_n+1): 35 | for k in range(n+1): 36 | if k in [0, n]: 37 | ch[n][k] = 1 38 | else: 39 | ch[n][k] = (ch[n-1][k-1] + ch[n-1][k]) % M 40 | return ch 41 | 42 | ########################################################################## 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ''' Verification test ''' 51 | if __name__ == "__main__": 52 | M = 1000000007; modinv = [-1 for _ in range(1001)] 53 | modinv[1] = 1 54 | for x in range(2, 1001): 55 | modinv[x] = (-(M//x) * modinv[M%x])%M 56 | 57 | for arg in [(5, 0), (0, 5), (5, 5), (132, 22), (123, 34)]: 58 | r1, r2 = choose(*arg), mod_choose(*arg, M, modinv) 59 | assert r1 % M == r2 60 | if arg[0] >= arg[1] >= 0: 61 | r3 = mod_choose_dp(arg[0], M)[arg[0]][arg[1]] 62 | assert r2 == r3 63 | print(' {:<3d} choose {:<18d} = {:d}'.format(*arg, r1)) 64 | print(' {:<3d} choose {:<3d} mod {:d} = {:d}'.format(*arg, M, r2)) 65 | -------------------------------------------------------------------------------- /Data-structures/Heap-custom-comparator.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Implements a min-heap with a custom comparator. Runtime comparable to heapq library. 3 | ''' 4 | 5 | ######################################################################### 6 | 7 | # custom comparator 8 | _less = lambda x, y: x < y 9 | 10 | def heapify(h, i): 11 | curr = i 12 | while curr < len(h): 13 | left, right = (curr << 1) + 1, (curr << 1) + 2 14 | if right >= len(h) or _less(h[left], h[right]): 15 | chil = left 16 | else: 17 | chil = right 18 | if chil >= len(h): 19 | break 20 | if not _less(h[curr], h[chil]): 21 | h[curr], h[chil] = h[chil], h[curr] 22 | curr = chil 23 | else: 24 | break 25 | 26 | def build_heap(h): 27 | for i in range(min(len(h), len(h)//2+1)-1, -1, -1): 28 | heapify(h, i) 29 | 30 | def _bubble_up(h, i): 31 | curr = i 32 | while curr > 0: 33 | par = (curr - 1) >> 1 34 | if _less(h[curr], h[par]): 35 | h[curr], h[par] = h[par], h[curr] 36 | curr = par 37 | else: 38 | break 39 | 40 | def heap_add(h, v): 41 | h.append(v) 42 | _bubble_up(h, len(h)-1) 43 | 44 | def heap_pop(h): 45 | if len(h) > 0: 46 | h[0], h[-1] = h[-1], h[0] 47 | ret = h.pop() 48 | heapify(h, 0) 49 | return ret 50 | 51 | ######################################################################### 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ''' Verification test ''' 61 | if __name__ == "__main__": 62 | # compare aganst the heapq library 63 | from random import randint 64 | import heapq as hq 65 | 66 | a = [randint(1, 10000) for _ in range(1000)] 67 | b = list(a) 68 | hq.heapify(a) 69 | build_heap(b) 70 | assert a == b 71 | 72 | while len(a) > 10: 73 | assert hq.heappop(a) == heap_pop(b) 74 | assert a == b 75 | 76 | for _ in range(1000): 77 | t = randint(-1000, 1000) 78 | hq.heappush(a, t) 79 | heap_add(b, t) 80 | assert a == b 81 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Sieving-methods/Euler-totient-function.py: -------------------------------------------------------------------------------- 1 | ''' Sieve for the Euler totient function. ''' 2 | 3 | ######################################################################### 4 | 5 | def fast_prime_sieve(n): 6 | """ 7 | Input n>=6, Returns a list of primes, 2 <= p <= n 8 | Taken from: https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/ 9 | """ 10 | n += 1 11 | n, correction = n-n%6+6, 2-(n%6>1) 12 | sieve = [True] * (n//3) 13 | for i in range(1,int(n**0.5)//3+1): 14 | if sieve[i]: 15 | k=3*i+1|1 16 | sieve[ k*k//3 ::2*k] = [False] * ((n//6-k*k//6-1)//k+1) 17 | sieve[k*(k-2*(i&1)+4)//3::2*k] = [False] * ((n//6-k*(k-2*(i&1)+4)//6-1)//k+1) 18 | return [2,3] + [3*i+1|1 for i in range(1,n//3-correction) if sieve[i]] 19 | 20 | 21 | def euler_phi(n, primes): 22 | phi = [1] * (n+1); phi[0] = 0 23 | for p in primes: 24 | phi[p] = p - 1 25 | for i in range(2, n+1): 26 | for p in primes: 27 | if i*p > n: 28 | break 29 | if i % p == 0: 30 | phi[i * p] = phi[i] * p 31 | break 32 | else: 33 | phi[i * p] = phi[i] * phi[p] 34 | return phi 35 | 36 | 37 | def euler_phi_2(n): 38 | phi = list(range(n+1)) 39 | for p in range(2, n+1, 2): 40 | phi[p] >>= 1 41 | for p in range(3, n+1, 2): 42 | if phi[p] == p: 43 | phi[p] -= 1 44 | for j in range(2*p, n+1, p): 45 | phi[j] -= phi[j] // p 46 | return phi 47 | 48 | ######################################################################### 49 | 50 | 51 | 52 | 53 | 54 | 55 | ''' Verification test ''' 56 | if __name__ == "__main__": 57 | assert euler_phi_2(12312)[-1] == 3888 58 | 59 | ''' 60 | Approximate runtime (Computing euler_phi_2(n)): 61 | _______________________________________________ 62 | n time 63 | 100 1e-4s 64 | 10**4 0.01s 65 | 10**6 0.045s 66 | 10**8 6.8s 67 | ''' 68 | -------------------------------------------------------------------------------- /Mathematics/Number-theory/Sieving-methods/Totient-summatory-function.py: -------------------------------------------------------------------------------- 1 | ''' The Euler"s totient summatory function Phi. ''' 2 | 3 | ######################################################################### 4 | 5 | import itertools as it 6 | 7 | def fast_prime_sieve(n): 8 | """ 9 | Input n>=6, Returns a list of primes, 2 <= p <= n 10 | Taken from: https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/ 11 | """ 12 | n += 1 13 | n, correction = n-n%6+6, 2-(n%6>1) 14 | sieve = [True] * (n//3) 15 | for i in range(1,int(n**0.5)//3+1): 16 | if sieve[i]: 17 | k=3*i+1|1 18 | sieve[ k*k//3 ::2*k] = [False] * ((n//6-k*k//6-1)//k+1) 19 | sieve[k*(k-2*(i&1)+4)//3::2*k] = [False] * ((n//6-k*(k-2*(i&1)+4)//6-1)//k+1) 20 | return [2,3] + [3*i+1|1 for i in range(1,n//3-correction) if sieve[i]] 21 | 22 | # sieve of phi 23 | def euler_phi(n, primes): 24 | phi = [1] * (n+1); phi[0] = 0 25 | for p in primes: 26 | phi[p] = p - 1 27 | for i in range(2, n+1): 28 | for p in primes: 29 | if i*p > n: 30 | break 31 | if i % p == 0: 32 | phi[i * p] = phi[i] * p 33 | break 34 | else: 35 | phi[i * p] = phi[i] * phi[p] 36 | return phi 37 | 38 | 39 | # roughly O(n^2/3) 40 | def Phi(n): 41 | ''' Returns two arrays Phi1, Phi2: 42 | Phi1[i] = Phi(i) for i <= sqrt(n) 43 | Phi2[i] = Phi(n // i) for i <= sqrt(n) 44 | ''' 45 | L = int(n**0.62) 46 | phis = euler_phi(L, fast_prime_sieve(L)) 47 | Phi1 = list(it.accumulate(phis)) # Phi1[i] = Phi(i) 48 | Phi2 = [0] * (n//L+1) # Phi2[i] = Phi(n//i) 49 | for j in range(n//L, 0, -1): 50 | k = n//j 51 | ksqrt = int(k**0.5) 52 | v = 0 53 | for i in range(ksqrt, 1, -1): 54 | kdivi = k//i 55 | mult = kdivi - k//(i+1) 56 | if kdivi > L: 57 | v -= Phi2[n//kdivi] 58 | else: 59 | v -= Phi1[kdivi] 60 | v -= mult*Phi1[i] 61 | mult = k - k//2 62 | v -= mult*Phi1[1] 63 | if k//ksqrt == ksqrt: 64 | v += Phi1[ksqrt] 65 | Phi2[j] = v + k*(k+1)//2 66 | return Phi1, Phi2 67 | 68 | ######################################################################### 69 | 70 | 71 | 72 | 73 | ''' 74 | Approximate runtime (Computing pi(n)): 75 | _______________________________________________ 76 | n time 77 | 100 1e-4s 78 | 10**4 1e-3s 79 | 10**6 0.015s 80 | 10**8 0.06s 81 | 10**10 5s 82 | ''' 83 | -------------------------------------------------------------------------------- /Data-structures/Segment-tree-lazy.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Segment tree with lazy propagation. Supports two main operations: 3 | - Update all elements in range [l, r] to a new value 4 | - Query a given operation (min, max, sum, ...) on range [l, r] 5 | ''' 6 | 7 | ######################################################################### 8 | 9 | 10 | def build_tree(node, start, end, op=(lambda x,y: x+y)): 11 | if start == end: 12 | tree[node] = arr[start] 13 | else: 14 | mid = (start + end) >> 1 15 | build_tree(node<<1, start, mid) 16 | build_tree(node<<1|1, mid + 1, end) 17 | tree[node] = op(tree[node<<1], tree[node<<1|1]) 18 | 19 | def update_range(node, start, end, l, r, val, op=(lambda x,y: x+y)): 20 | if lazy[node]: 21 | tree[node] = lazy[node] 22 | if start != end: 23 | lazy[node<<1] = lazy[node] 24 | lazy[node<<1|1] = lazy[node] 25 | lazy[node] = 0 26 | if start > end or start > r or end < l: 27 | return 28 | if start >= l and end <= r: 29 | tree[node] = val 30 | if start != end: 31 | lazy[node<<1] = val 32 | lazy[node<<1|1] = val 33 | return 34 | mid = (start + end) >> 1 35 | update_range(node<<1, start, mid, l, r, val) 36 | update_range(node<<1|1, mid + 1, end, l, r, val) 37 | tree[node] = op(tree[node<<1], tree[node<<1|1]) 38 | 39 | def query_range(node, start, end, l, r, op=(lambda x,y: x+y), out_of_bound_value=0): 40 | if start > end or start > r or end < l: 41 | return out_of_bound_value 42 | if lazy[node]: 43 | tree[node] = lazy[node] 44 | if start != end: 45 | lazy[node<<1] = lazy[node] 46 | lazy[node<<1|1] = lazy[node] 47 | lazy[node] = 0 48 | if start >= l and end <= r: 49 | return tree[node] 50 | mid = (start + end) >> 1 51 | return op(query_range(node<<1, start, mid, l, r), query_range(node<<1|1, mid + 1, end, l, r)) 52 | 53 | def update(l, r, val): 54 | ''' Updates all elements in [l, r] to val. inclusive, indexed from 0 ''' 55 | update_range(1, 0, len(arr) - 1, l, r, val) 56 | 57 | def query(l, r): 58 | ''' inclusive, indexed from 0''' 59 | return query_range(1, 0, len(arr) - 1, l, r) 60 | 61 | 62 | 63 | arr = [] 64 | tree = [-1 for _ in range(len(arr)<<2)] 65 | lazy = [0 for _ in range(len(arr)<<2)] 66 | 67 | ######################################################################### 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | ''' Verification test ''' 80 | if __name__ == "__main__": 81 | # Passed 82 | # https://www.hackerearth.com/practice/data-structures/advanced-data-structures/fenwick-binary-indexed-trees/practice-problems/algorithm/help-ashu-1/editorial/ 83 | if 0: 84 | n = int(input()) 85 | arr = list(map(lambda x: int(x)&1, input().split())) 86 | 87 | tree = [-1 for _ in range(len(arr)<<2)] 88 | lazy = [0 for _ in range(len(arr)<<2)] 89 | build_tree(1, 0, len(arr) - 1) 90 | 91 | for _ in range(int(input())): 92 | q, a, b = map(int, input().split()) 93 | # print(q, a, b) 94 | if q == 0: 95 | update(a-1, a-1, b&1) 96 | elif q == 1: 97 | print(b - a + 1 - query(a-1, b-1)) 98 | else: 99 | print(query(a-1, b-1)) 100 | -------------------------------------------------------------------------------- /Mathematics/Continued-fractions.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Utility functions for working with continued fractions representations. 3 | ''' 4 | 5 | 6 | 7 | 8 | ######################################################################### 9 | 10 | def continued_fraction_to_fraction(cf): 11 | ''' Converts a continued fraction into fraction. ''' 12 | cf += [0,0] 13 | convergents = [(cf[0],1), (cf[1]*cf[0] + 1, cf[1])] 14 | for i in range(2, len(cf)): 15 | h = cf[i]*convergents[-1][0] + convergents[-2][0] 16 | k = cf[i]*convergents[-1][1] + convergents[-2][1] 17 | convergents.append((h, k)) 18 | return convergents[-1] 19 | 20 | def fraction_to_continued_fraction(f, precision=100): 21 | ''' Converts a fraction into continued fraction. ''' 22 | n, d = f 23 | cf = [] 24 | for _ in range(precision): 25 | cf.append(n//d) 26 | n, d = d, n - cf[-1]*d 27 | if not d: 28 | break 29 | return cf 30 | 31 | 32 | def continued_fraction_square_root(n, precision=100): 33 | ''' 34 | Given an integer n, returns continued fraction for \sqrt(n). 35 | ''' 36 | # perfect square 37 | if int(n ** 0.5) == n ** 0.5: 38 | return [int(n ** 0.5)] 39 | 40 | m, d, a = 0, 1, int(n ** 0.5) 41 | cf = [a] 42 | for _ in range(precision): 43 | m = d*a - m 44 | d = (n - m*m)//d 45 | a = int((n ** 0.5 + m)/d) 46 | cf.append(a) 47 | return cf 48 | 49 | 50 | def best_approximation(cf, den_bound=10**12): 51 | ''' Given a continued fraction, returns its best rational approximation in which 52 | the denominator is <= to the den_bound. 53 | For details see Wikipedia article on continued fractions. 54 | ''' 55 | best = (-1, -1) 56 | convergents = [(cf[0],1), (cf[1]*cf[0] + 1, cf[1])] 57 | for i in range(2, len(cf)): 58 | h = cf[i]*convergents[-1][0] + convergents[-2][0] 59 | k = cf[i]*convergents[-1][1] + convergents[-2][1] 60 | 61 | startm = cf[i]//2 + 1 if cf[i]&1 else cf[i]//2 62 | if not cf[i]&1: 63 | ln, ld = continued_fraction_to_fraction(cf[1:i+1][::-1]) 64 | rn, rd = continued_fraction_to_fraction(cf[i:]) 65 | if not ln * rd > rn * ld: 66 | startm += 1 67 | 68 | for m in range(startm, cf[i]+1): 69 | sh = m*convergents[-1][0] + convergents[-2][0] 70 | sk = m*convergents[-1][1] + convergents[-2][1] 71 | if sk <= den_bound: 72 | best = (sh, sk) 73 | else: 74 | return best 75 | 76 | convergents.append((h, k)) 77 | return best 78 | 79 | 80 | ######################################################################### 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | ''' Verification test ''' 89 | if __name__ == "__main__": 90 | from fractions import Fraction 91 | print('Test 1:') 92 | for cf in [[0], [-1], [5], [1, 2], [1, 2, 3], [4, 2, 6, 7]]: 93 | print(' Continued fraction: {:<12s} -> {:s}'.format(str(cf), str(Fraction(*continued_fraction_to_fraction(cf))))) 94 | print('') 95 | 96 | print('Test 2: ') 97 | for f in [(0, 1), (-1, 1), (5, 1), (3, 2), (10, 7), (415, 93)]: 98 | print(' Fraction: {:<12s} -> {:s}'.format(str(Fraction(*f)), str(fraction_to_continued_fraction(f)))) 99 | print('') 100 | 101 | print('Test 3: ') 102 | for i in list(range(10)) + [19]: 103 | print(' N = {:<5d} -> sqrt N = {:s}'.format(i, str(continued_fraction_square_root(i, 10)))) 104 | print('') 105 | 106 | print('Test 4: ') 107 | cf = [0, 1, 5, 2, 2] 108 | f = continued_fraction_to_fraction(cf) 109 | print(' {:s} = {:s} = {:.5f}'.format(str(cf), str(Fraction(*f)), f[0]/f[1])) 110 | for i in range(1, 35): 111 | print(' Best rat. approx. with denominator bound {:d}: {:s}'.format(i, str(Fraction(*best_approximation(cf, i))))) 112 | -------------------------------------------------------------------------------- /Data-structures/Segment-tree.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Segment tree with lazy propagation. Supports two main operations: 3 | - Update all elements in range [l, r] to a new value 4 | - Query a given operation (min, max, sum, ...) on range [l, r] 5 | ''' 6 | 7 | ######################################################################### 8 | 9 | # Short implementation 10 | 11 | def build_tree(node, start, end, op=(lambda x,y: x+y)): 12 | if start == end: 13 | tree[node] = arr[start] 14 | else: 15 | mid = (start + end) >> 1 16 | build_tree(node<<1, start, mid) 17 | build_tree(node<<1|1, mid + 1, end) 18 | tree[node] = op(tree[node<<1], tree[node<<1|1]) 19 | 20 | def update_range(node, start, end, l, r, val, op=(lambda x,y: x+y)): 21 | if start > end or start > r or end < l: 22 | return 23 | if start >= l and end <= r: 24 | tree[node] = val 25 | return 26 | mid = (start + end) >> 1 27 | update_range(node<<1, start, mid, l, r, val) 28 | update_range(node<<1|1, mid + 1, end, l, r, val) 29 | tree[node] = op(tree[node<<1], tree[node<<1|1]) 30 | 31 | def query_range(node, start, end, l, r, op=(lambda x,y: x+y), out_of_bound_value=0): 32 | if start > end or start > r or end < l: 33 | return out_of_bound_value 34 | if start >= l and end <= r: 35 | return tree[node] 36 | mid = (start + end) >> 1 37 | return op(query_range(node<<1, start, mid, l, r), query_range(node<<1|1, mid + 1, end, l, r)) 38 | 39 | def update(l, r, val): 40 | ''' Updates all elements in [l, r] to val. inclusive, indexed from 0 ''' 41 | update_range(1, 0, len(arr) - 1, l, r, val) 42 | 43 | def query(l, r): 44 | ''' inclusive, indexed from 0''' 45 | return query_range(1, 0, len(arr) - 1, l, r) 46 | 47 | arr = [] 48 | tree = [-1 for _ in range(len(arr)<<2)] 49 | build_tree(1, 0, len(arr) - 1) 50 | 51 | 52 | 53 | 54 | 55 | ######################################################################### 56 | 57 | # Class ortiented implementation 58 | 59 | class Node(object): 60 | def __init__(self, start, end): 61 | self.start, self.end = start, end 62 | self.total, self.left, self.right = 0, None, None 63 | 64 | class SegmentTree(object): 65 | def operation(self, x, y): 66 | return max(x, y) 67 | 68 | def __init__(self, nums): 69 | def createTree(nums, l, r): 70 | if l > r: 71 | return None 72 | if l == r: 73 | n = Node(l, r) 74 | n.total = nums[l] 75 | return n 76 | mid = (l + r) // 2 77 | root = Node(l, r) 78 | root.left = createTree(nums, l, mid) 79 | root.right = createTree(nums, mid+1, r) 80 | root.total = self.operation(root.left.total, \ 81 | root.right.total) 82 | return root 83 | self.root = createTree(nums, 0, len(nums)-1) 84 | 85 | def update(self, i, val): 86 | def updateVal(root, i, val): 87 | if root.start == root.end: 88 | root.total = val 89 | return val 90 | mid = (root.start + root.end) // 2 91 | if i <= mid: 92 | updateVal(root.left, i, val) 93 | else: 94 | updateVal(root.right, i, val) 95 | root.total = self.operation(root.left.total, \ 96 | root.right.total) 97 | return root.total 98 | return updateVal(self.root, i, val) 99 | 100 | def sumRange(self, i, j): 101 | def rangeSum(root, i, j): 102 | if root.start == i and root.end == j: 103 | return root.total 104 | mid = (root.start + root.end) // 2 105 | if j <= mid: 106 | return rangeSum(root.left, i, j) 107 | elif i >= mid + 1: 108 | return rangeSum(root.right, i, j) 109 | else: 110 | return self.operation(rangeSum(root.left, i, \ 111 | mid), rangeSum(root.right, mid+1, j)) 112 | return rangeSum(self.root, i, j) 113 | 114 | 115 | 116 | ######################################################################### 117 | 118 | 119 | 120 | 121 | 122 | 123 | ''' Verification test ''' 124 | if __name__ == "__main__": 125 | # Passed 126 | # https://www.hackerearth.com/practice/data-structures/advanced-data-structures/fenwick-binary-indexed-trees/practice-problems/algorithm/help-ashu-1/editorial/ 127 | pass 128 | -------------------------------------------------------------------------------- /Data-structures/Treap.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Binary Search Tree type of data structure with logarithmic height (with high probability). 3 | Combines the BST and heap properties. 4 | 5 | Also support k-th element and median() operations. 6 | ''' 7 | 8 | 9 | ######################################################################### 10 | 11 | import random 12 | 13 | class TreapNode(object): 14 | def __init__(self, key): 15 | self.key = key 16 | self.ran = random.random() 17 | self.size = 1 18 | self.cnt = 1 19 | self.left = None 20 | self.right = None 21 | 22 | def left_rotate(self): 23 | a = self 24 | b = a.right 25 | a.right = b.left 26 | b.left = a 27 | a = b 28 | b = a.left 29 | b.size = b.left_size() + b.right_size() + b.cnt 30 | a.size = a.left_size() + a.right_size() + a.cnt 31 | return a 32 | 33 | def right_rotate(self): 34 | a = self 35 | b = a.left 36 | a.left = b.right 37 | b.right = a 38 | a = b 39 | b = a.right 40 | b.size = b.left_size() + b.right_size() + b.cnt 41 | a.size = a.left_size() + a.right_size() + a.cnt 42 | return a 43 | 44 | def left_size(self): 45 | return 0 if self.left is None else self.left.size 46 | 47 | def right_size(self): 48 | return 0 if self.right is None else self.right.size 49 | 50 | class Treap(object): 51 | def __init__(self): 52 | self.root = None 53 | 54 | def _insert(self, node, key): 55 | if node is None: 56 | node = TreapNode(key) 57 | return node 58 | node.size += 1 59 | if key < node.key: 60 | node.left = self._insert(node.left, key) 61 | if node.left.ran < node.ran: 62 | node = node.right_rotate() 63 | elif key >= node.key: 64 | node.right = self._insert(node.right, key) 65 | if node.right.ran < node.ran: 66 | node = node.left_rotate() 67 | return node 68 | 69 | def insert(self, key): 70 | self.root = self._insert(self.root, key) 71 | 72 | def _find(self, node, key): 73 | if node == None: 74 | return None 75 | if node.key == key: 76 | return node 77 | if key < node.key: 78 | return self._find(node.left, key) 79 | else: 80 | return self._find(node.right, key) 81 | 82 | def find(self, key): 83 | return self._find(self.root, key) 84 | 85 | def _delete(self, node, key): 86 | if node is None: 87 | return False 88 | if node.key == key: 89 | if node.left is None and node.right is None: 90 | return None 91 | elif node.left is None: 92 | return node.right 93 | elif node.right is None: 94 | return node.left 95 | else: 96 | if node.left.ran < node.right.ran: 97 | node = node.right_rotate() 98 | node.right = self._delete(node.right, key) 99 | else: 100 | node = node.left_rotate() 101 | node.left = self._delete(node.left, key) 102 | elif key < node.key: 103 | node.left = self._delete(node.left, key) 104 | else: 105 | node.right = self._delete(node.right, key) 106 | node.size = node.left_size() + node.right_size() + node.cnt 107 | return node 108 | 109 | def delete(self, key): 110 | if self.find(key) is None: return False 111 | self.root = self._delete(self.root, key) 112 | return True 113 | 114 | def _find_kth(self, node, k): 115 | if node is None: return None 116 | if k <= node.left_size(): 117 | return self._find_kth(node.left, k) 118 | if k > node.left_size() + node.cnt: 119 | return self._find_kth(node.right, k - node.left_size() - node.cnt) 120 | return node 121 | 122 | def find_kth(self, k): 123 | if k <=0 or k > self.size(): 124 | return None 125 | return self._find_kth(self.root, k) 126 | 127 | def size(self): 128 | return 0 if self.root is None else self.root.size 129 | 130 | def median(self): 131 | s = self.size() 132 | if s == 0: return 0 133 | result = 0 134 | if s % 2 == 1: 135 | result = self.find_kth(s // 2 + 1).key 136 | else: 137 | result = (self.find_kth(s // 2).key + self.find_kth(s // 2 + 1).key) / 2.0 138 | if result == int(result): result = int(result) 139 | return result 140 | 141 | ######################################################################### 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | ''' Verification test ''' 153 | if __name__ == "__main__": 154 | from random import shuffle, randint 155 | from time import clock 156 | from heapq import nsmallest 157 | for l in range(1, 7): 158 | arr = list(range(1, 10**l)) 159 | k = randint(1, len(arr)) 160 | shuffle(arr) 161 | 162 | start = clock() 163 | T = Treap() 164 | for x in arr: 165 | T.insert(x) 166 | print('Treap building', clock() - start) 167 | start = clock() 168 | assert k == T.find_kth(k).key 169 | print('Treap find_kth for length 10 ^', l , '; k =', k, clock() - start) 170 | 171 | start = clock() 172 | n = nsmallest(k, arr)[k-1] 173 | assert n == k 174 | print('heapq for length 10 ^', l , ': k =', k, clock() - start) 175 | --------------------------------------------------------------------------------