Learn & Master Maths for Competitive Programming Math Prime Numbers Basic Approach to Find a Prime Number Optimized Approach to Find a Prime Number Sieve of Eratosthenes Prime Factorization GCD and LCM Euclidean Division Euclidean Subtraction Extended Euclidean Algorithm LCM Modular Arithmetic Properties Modulo of Negative Numbers Modular Multiplicative Inverse Binary Exponentiation Euler's Totient Function Fermat's Little Theorem Permutation and Combination Permutation Combination Program to calculate value of nCr Binomial Coefficient Ways to sum to N using Natural Numbers up to K with repetitions allowed Count ways to place M objects in distinct partitions of N boxes Probabilities Probability of an Event Theorems: Count all unique outcomes possible by performing S flips on N coins Number System How to convert a number from one base to another? Linear Diophantine Equations Prime Numbers Prime Numbers A prime number is a natural number greater than 1, which is only divisible by 1 and itself i.e., it has only two factors. For Example: 2, 3, 5, 7, 11 Some Important Facts to Know: 1. Numbers which have more than two factors are called composite numbers. 1 is neither prime nor composite. It is a coprime number. 2. Every prime number can be represented in the form of 6n+1 or 6n-1, where n is any natural number; except the prime numbers 2 and 3. 3. 2 is the only even prime number. 4. 2 and 3 are the only two prime numbers that are consecutive natural numbers. 5. Prime Number Theorem: The probability that a given, randomly chosen number n is prime is inversely proportional to its number of digits, or to the logarithm of n. Goldbach Conjecture: Every even integer greater than 2 can be expressed as the sum of two primes. 6. Lemoine’s Conjecture: Any odd integer greater than 5 can be expressed as a sum of an odd prime (all primes other than 2 are odd) and an even semiprime. A semiprime number is a product of two prime numbers. This is called Lemoine’s conjecture. 7. Wilson Theorem: A natural number p > 1 is a prime number if and only if (p - 1) ! -1 mod p (p - 1) ! (p-1) mod p 8. Fermat’s Little Theorem: If n is a prime number, then for every a, 1 <= a < n, a -1 1 (mod n) a -1 % n = 1 Basic Approach to Find a Prime Number 1. Brute Force Iterate through all numbers from 2 to n-1 and for every number check if it divides n. If we find any number that divides, it means it is the factor of the given number. Hence, we return false. Code bool isPrime(int n) { // check if it is natural number if (n <= 1) return false; // Check from 2 to n-1 for (int i = 2; i < n; i++) if (n % i == 0) return false; } return true; Time Complexity: O(N) 2. Recursion Recursion is used to check if a number between 2 to n – 1 divides n. If we find any number that divides, we return false.. Code bool isPrime(int n) { static int i = 2; // corner cases if (n <= 1) { return false; } // Checking if it is number 2 if (n == i) return true; // base case if (n % i == 0) { return false; } i++; return isPrime(n); //Recursive Case } Time Complexity: O(N) Space Complexity: O(N) Optimized Approach to Find a Prime Number 1. Square Root As we iterate from 2 to n-1, we realize that after some point factorization becomes exactly the same but in the reverse order. For Example: n = 64 2 * 32 4 * 16 8*8 16 * 4 32 * 2 Here, 8 * 8 acts like a mirror. On analyzing various other examples, it was found that the line which divided the factors into two halves is the line of the square root of the number. If the number is a perfect square, we can shift the line by 1 and if we can get the integer value of the line which divides. Now our iterations have reduced to half and we will iterate only till the square root of the number. For this we will calculate the square root of the number and get its floor value using the math library of Python and iterate till then. Code bool isPrime(int n) { // Checking for natural number if (n <= 1) return false; // Check from 2 to square root of n for (int i = 2; i <= sqrt(n); i++) if (n % i == 0) return false; return true; } 2. Odd Divisors In this method, we will check only the odd divisors for divisibility. Because we know that even numbers except 2 can’t be prime numbers since, they are divisible by 2. Code bool isPrime(int n) { // Checking for natural number if (n <= 1) return false; } // Checking for even natural number if (n > 2 && n % 2 == 0) return false // Check all odd numbers from 3 to square root of n for (int i = 3; i <= sqrt(n); i+2) if (n % i == 0) return false; return true; Sieve of Eratosthenes The sieve of Eratosthenes is used to find all primes smaller than n where n is smaller than 10 million or so. It is efficient but cannot be used to check if the given number is Prime or not. Algorithm 1. Create a list of consecutive integers from 2 through n: (2, 3, 4, ..., n). 2. Initially, let p equal 2, the smallest prime number. 3. Enumerate the multiples of p by counting in increments of p from 2p to n, and mark them in the list (these will be 2p, 3p, 4p, ...; the p itself should not be marked). 4. Find the smallest number in the list greater than p that is not marked. If there was no such number, stop. Otherwise, let p now equal this new number (which is the next prime), and repeat from step 3. 5. When the algorithm terminates, the numbers remaining not marked in the list are all the primes below n. Because if it were composite, it would be marked as a multiple of some other, smaller prime. Note: Some of the numbers are marked more than once (e.g.,15 will be marked both for 3 and 5). To improvise, it is sufficient to mark the numbers in step 3 starting from p2, as all the smaller multiples of p will have already been marked at that point. This means that the algorithm is allowed to terminate in step 4 when p2 is greater than n. Another improvisation is to initially list odd numbers only, (3, 5, ..., n), and count in increments of 2p from p2 in step 3, thus marking only odd multiples of p. This actually appears in the original algorithm. It can be generalized with wheel factorization, forming the initial list only from numbers coprime with the first few primes and not just from odds (i.e., numbers coprime with 2), and counting in the correspondingly adjusted increments so that only such multiples of p are generated that are coprime with those small primes, in the first place. Example For Example: n = 30. We need to print all prime numbers smaller than or equal to 30. 1. We first create a list of all numbers from 2 to 30 except 1 since it is coprime. 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 2. Take the first number 2. Mark all the numbers which are greater than and divisible by 2 or equal to the square of it. 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3. Progress towards the next unmarked number 3. Mark all the numbers which than and divisible by 3 or equal to the square of it. 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 are greater 4. The next unmarked number is 5. Mark all the numbers which are greater than and divisible by 5 or equal to the square of it. 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 4. Continue this process till you reach the end of the table. Your final table looks like below: 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 So, the prime numbers are the unmarked ones: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29. Pseudocode input an integer n > 1. let A be an array of Boolean values, indexed by integers 2 to n, initially all set to true. for i = 2, 3, 4, ..., not exceeding √n do if A[i] is true for j = i2, i2+i, i2+2i, i2+3i, ..., not exceeding n do A[j]:= false return all i such that A[i] is true. This pseudocode produces all primes not greater than n. It includes a common optimization, which is to start enumerating the multiples of each prime i from i2. Code void SieveOfEratosthenes(int n){ // Create a boolean array "prime[0..n]" // initialize all entries as true. // A value in prime[i] will // finally be false if i is // Not a prime, else true. bool prime[n + 1]; memset(prime, true, sizeof(prime)); 2. Prime factorizations help us to find divisibility, simplifying fractions, and common denominators for fractions. 3. To find prime factorization for a large composite number, we use Pollard’s Rho algorithm. 4. Prime Factorization is applied in Cryptography hence is very important to people who try to make (or break) secret codes based on numbers. Prime factorization using Naive Solution Algorithm: 1. While n is divisible by 2, print 2 and divide n by 2. 2. After step 1, n must be odd. Now start a loop from i = 3 to square root of n. While i divides n, print i and divide n by i, increment i by 2 and continue. 3. If n is a prime number and is greater than 2, then n will not become 1 by above two steps. So print n if it is greater than 2. Explanation: First it takes care of even numbers. We extract all the 2 that are involved in prime factorization. All remaining prime factors must be odd (difference of two prime factors must be at least 2), this is why i is incremented by 2. Then it extracts all the odd prime factors. The loop runs till the square root of n because: As per the property of composite numbers, every composite number has at least one prime factor less than or equal to the square root of itself. This property can be proved using a counter statement. Let a and b be two factors of n such that a*b = n. If both are greater than √n, then a.b > √n * √n, which contradicts the expression “a * b = n”. In the above algorithm, we run a loop and do followinga. Find the least prime factor i (must be less than √n, ) b. Remove all occurrences i from n by repeatedly dividing n by i. c. Repeat steps a and b for divided n and i = i + 2. The steps a and b are repeated till n becomes either 1 or a prime number. Code void primeFactors(int n) { // Print the number of 2s that divide n while (n%2 == 0) { printf("%d ", 2); n = n/2; } // n must be odd at this point. // Note i = i +2 So we can skip one element for (int i = 3; i <= sqrt(n); i = i+2) { // While i divides n, print i and divide n while (n%i == 0) { printf("%d ", i); n = n/i; } } } // This condition is to handle the case when n // is a prime number greater than 2 if (n > 2) printf ("%d ", n); Time complexity: O(√n)Space Prime factorization using Naive Solution Store the Smallest Prime Factor (SPF) for every number. Then calculate the prime factorization of the given number by dividing the given number recursively with its smallest prime factor till it becomes 1. To calculate the smallest prime factor for every number we will use concepts of “Sieve of Eratosthenes”. In this, every time we mark a number as non-prime, we store the corresponding smallest prime factor for that number. Now, after we are done with pre-calculating the smallest prime factor for every number, we will divide our number n (whose prime factorization is to be calculated) by its corresponding smallest prime factor till n becomes 1. Pesudocode PrimeFactors[] // To store result i = 0 // Index in PrimeFactors while n != 1 : // SPF : smallest prime factor PrimeFactors[i] = SPF[n] i++ n = n / SPF[n] Code #define max 100001 // stores smallest prime factor for every number int spf[max]; // Calculating SPF (Smallest Prime Factor) for every number till max. void sieve() { spf[1] = 1; for (int i=2; i<max; i++) // marking smallest prime factor for every // number to be itself. spf[i] = i; // separately marking spf for every even // number as 2 for (int i=4; i<max; i+=2) spf[i] = 2; for (int i=3; i*i<max; i++) { // checking if i is prime if (spf[i] == i) { // marking SPF for all numbers divisible by i for (int j=i*i; j<max; j+=i) } } // marking spf[j] if it is not // previously marked if (spf[j]==j) spf[j] = i; } // Function returning prime factorization by dividing by smallest prime factor at every step vector<int> getFactorization(int x) { } vector<int> ret; while (x != 1) { ret.push_back(spf[x]); x = x / spf[x]; } return ret; // driver program for above function int main(int argc, char const *argv[]) { // precalculating Smallest Prime Factor sieve(); int x = 12246; cout << "prime factorization for " << x << " : "; // calling getFactorization function vector <int> p = getFactorization(x); } for (int i=0; i<p.size(); i++) cout << p[i] << " "; cout << endl; return 0; Output: prime factorization for 12246: 2 3 13 157 Note: The above code works well for n up to the order of 107. Beyond this we will face memory issues. Time Complexity: O(log n) Smallest prime factor is calculated in O(n log log n) using Sieve. Whereas in the calculation step, we divide the number every time by the smallest prime number till it becomes 1. Let’s assume the worst case in which every time the SPF is 2, we will have log n division steps. Hence, time complexity will be O(log n) in the worst case. GCD and LCM GCD (Greatest Common Divisor) or HCF (Highest Common Factor) of two numbers is the largest number that divides both of them. For Example: GCD of 20 and 28 is 4 and GCD of 98 and 56 is 14. Basic approach could be to find all prime factors of both numbers, then find common factors and return the largest of them all. To optimize it, one can use the Euclidean algorithm which is the main algorithm used for this purpose. The idea is, the GCD of two numbers doesn’t change if a smaller number is subtracted from a bigger number. Code int gcd(int a, int b) { // Everything divides 0 if (a == 0) return b; if (b == 0) return a; // base case if (a == b) return a; // a is greater if (a > b) return gcd(a-b, b); return gcd(a, b-a); } Euclidean Division Algorithm: 1. Divide the greater number (divisible) by the smaller (divisor) and take the remainder 2. Since the remainder is not zero, the divisor turns into a divisible, and the remainder is a divisor 3. When the remainder is zero, the divisor is the desired GCD for a pair of given numbers. In this algorithm, the division is repeated until the remainder is zero. When it becomes so, the GCD is the divisor of the last division. Example: Find the GCD (106, 16): 1. 106 / 16 = 6, the remainder is 10 2. 16 / 10 = 1, the remainder is 6 3. 10 / 6 = 1, the remainder is 4 4. 6 / 4 = 1, the remainder is 2 5. 4 / 2 = 2, the remainder is 0 6. GCD (106, 16) = 2 106 / 16 = 6, remainder 10 16 / 10 = 1, remainder 6 10 / 6 = 1, remainder 4 6 / 4 = 1, remainder 2 4 / 2 = 2, remainder 0 GCD Code int gcd(int a, int b) { if (a == 0) return b; return gcd(b % a, a); } Time Complexity: O(Log min(a, b)) Euclidean Subtraction Algorithm: The algorithm is similar to the division method, but only works for positive integers. At each next step, the subtrahend and the difference from the previous step are taken. In this case, always less is subtracted from a larger number. Example: Find the GCD (108, 72): 1. 108 - 72 = 36 2. 72 - 36 = 36 3. 36 - 36 = 0 4. GCD (108, 72) = 36 Code int gcd(int a, int b) { if (a == 0) return b; return gcd(b - a, a); } Time Complexity: O(Log min(a, b)) Note: This algorithm is sometimes described in a different way. Subtraction ends earlier, in a step where one number divides another completely. That is, we combine the subtraction with a divisibility check. Example: Find the GCD (44, 60): 1. Does 44 divide 60 without remainder? No. 60 - 44 = 16. 2. Does 16 divide 44 without remainder? No. 44 - 16 = 28. 3. Does 16 divide 28 without remainder? No. 28 - 16 = 12. 4. Does 12 divide 16 without remainder? No. 16 - 12 = 4. 5. Does 4 divide 12 without remainder? Yes. Then GCD (44, 60) = 4. Explanation: If one natural number from a pair completely divides another, then their GCD will be equal to the smaller of them: if a / b without remainder, then GCD (a, b) = b. For Example: GCD (15, 5) = 5. Thus, if we come to a pair of numbers, one of which divides completely the other, the smaller will be the greatest common divisor for both. Euclid's algorithm looks for exactly such a pair of numbers. The second fact. It is required to prove that if one number is greater than another, their greatest common divisor is equal to the greatest common divisor for the smaller number from the pair, and the difference of the larger and smaller numbers. This can be written as: if a < b, then GCD(a, b) = GCD(a, b - a) The proof of GCD (a, b) = GCD (a, b - a) is as follows: Let b - a = c. If any number x divides without remainder both a and b, then it will also divide c completely. Because if a and b are not equal, then the divisor in them fits the whole, but a different number of times. And if you subtract one from the other, then the divisor must also fit an integer number of times into the resulting difference. x = GCD b a c=b-a c d e d=a-c e=c-d x=e-d If we successively decrease a and b, then sooner or later we come to the value of the smaller of them, which will divide the larger number without remainder. The smaller number in this pair will be the greatest common divisor for the original pair of natural numbers. This is the Euclidean algorithm. Extended Euclidean Algorithm Extended Euclidean algorithm finds integer coefficients x and y such that: ax + by = gcd(a, b) Example: Input: a = 30, b = 20 Output: gcd = 10 x = 1, y = -1 (Note that 30*1 + 20*(-1) = 10) The extended Euclidean algorithm updates results of gcd(a, b) using the results calculated by recursive call gcd(b%a, a). 3. Let values of x and y calculated by the recursive call be x1 and y1. x and y are updated using the below expressions. 4. x = y1 - b/a * x1 y = x1 Code { } int gcdExtended(int a, int b, int *x, int *y) // Base Case if (a == 0) { *x = 0; *y = 1; return b; } int x1, y1; // To store results of recursive call int gcd = gcdExtended(b%a, a, &x1, &y1); // Update x and y using results of // recursive call *x = y1 - (b/a) * x1; *y = x1; return gcd; Explanation: As seen above, x and y are results for inputs a and b, a.x + b.y = gcd And x1 and y1 are results for inputs b%a and a (b%a).x1 + a.y1 = gcd When we put b%a = (b-( b/a ).a) in above, we get the following. Note that b/a is floor (b/a) (b - ( b/a ).a).x1 + a.y1 = gcd Above equation can also be written as below b.x1 + a.(y1 - ( b/a ).x1) = gcd After comparing coefficients of 'a' and 'b' from above, we get following x = y1 - b/a * x1 y = x1 The extended Euclidean algorithm is particularly useful when a and b are coprime (or gcd is 1). Since x is the modular multiplicative inverse of “a modulo b”, and y is the modular multiplicative inverse of “b modulo a”. In particular, the computation of the modular multiplicative inverse is an essential step in the RSA public-key encryption method. LCM LCM (Least Common Multiple) of two numbers is the smallest number which can be divided by both numbers. LCM of 32, 48 and 72 32 = 2 × 2 × 2 × 2 × 2 48 = 2 × 2 × 2 × 2 x 3 72 = 2 × 2 × 2 x 3 × 3 2 2 2 2 2 3 3 Basic approach is to find all prime factors of both numbers, then find the union of all factors present in both numbers and return the product of elements in union. But the optimized solution is to use the below formula for LCM of two numbers ‘a’ and ‘b’. a x b = LCM(a, b) * GCD (a, b) LCM(a, b) = (a x b) / GCD(a, b) Code long long gcd(long long int a, long long int b) { if (b == 0) return a; return gcd(b, a % b); } // Function to return LCM of two numbers long long lcm(int a, int b) { return (a / gcd(a, b)) * b; } Time Complexity: O(Log min(a, b)) Modular Arithmetict Modular arithmetic is related to the “mod” functionality. It's all about computation of “mod” of expressions where expressions may have digits and computational symbols of addition, subtraction, multiplication, division or any other. Properties 1. Quotient Remainder Theorem It states that, for any pair of integers a and b (b is positive), there exists two unique integers q and r such that: a = b*q + r where 0 <= r < b Example: If a = 20, b = 6 then q = 3, r = 2 20 = 6 × 3 + 2 2. Modular Addition Rule for modular addition is: (a + b) mod m = ((a mod m) + (b mod m)) mod m Example: (15 + 17) % 7 = ((15 % 7) + (17 % 7)) % 7 = (1 + 3) % 7 =4%7 =4 3. Modular Multiplication Rule for modular multiplication is: (a x b) mod m = ((a mod m) x (b mod m)) mod m Example: (12 × 13) % 5 = ((12 % 5) x (13 % 5)) % 5 = (2 × 3) % 5 =6%5 =1 4. Modular Division Modular division is totally different from modular addition, subtraction and multiplication. It also does not always exist. (a / b) mod m is ≠ ((a mod m) / (b mod m)) mod m This is calculated using following formula: (a / b) mod m = (a x (inverse of b if exists)) mod m 5. Modular Inverse The modular inverse of a mod m exists only if a and m are relatively prime i.e., gcd(a, m) = 1. Hence, for finding inverse of a under modulo m, if (a x b) mod m = 1 then b is modular inverse of a. Example: a = 5, m = 7 (5 × 3) % 7 = 1 Hence, 3 is the modulo inverse of 5 under 7. 6. Modular Exponentiation Finding ab mod m is the modular exponentiation. There are two approaches for this – recursive and iterative. Example: a = 5, b = 2, m = 7 (52) % 7 = 25 % 7 = 4 Modulo of Negative Numbers The modulo or often referred to as “mod” represents the remainder of a division. mod(a, n) = a - n * floor(a / n) Doing an integer division and then multiplying it again means finding the biggest number smaller than a that is divisible by n without a remainder. Subtracting this from a yield the remainder of the division and by that the modulo. Restricting Boundst In programming, the modulo operator (% or sometimes mod) often is used to restrict an index to the bounds of an array or length limited data structure. values = [ 3, 4, 5 ] index = 5 value_at_index = values[ index % values.length ] For the above example this means 5 mod 3 = 2 following the definition is 5 - floor(5/3)*3 = 2. This means that no matter the value index has, the array bounds are met. The rules of modulo on negative numbers depend on the language you are using. Language 13 mod 3 -13 mod 3 13 mod -3 -13 mod -3 C 1 -1 1 -1 PHP 1 -1 1 -1 RUST 1 -1 1 -1 SCALA 1 -1 1 -1 JAVA 1 -1 1 -1 JAVASCRIPT 1 -1 1 -1 RUBY 1 2 -2 -1 PYTHON 1 2 -2 -1 So, if you use the modulo operator to ensure correct bounds for accessing a collection, beware that some languages need a little more diligence. A simple and efficient way is to check the sign. Code int mod(a, b) { c = a % b return (c < 0) ? c + b : c } As another option, you could also apply the modulo twice. Code int mod(a, b) { (((a % b) + b) % b) } Even or Odd Test whether a number is odd or even using the modulo operator. Code bool is_odd(int n) { return n % 2 != 0; // could be 1 or -1 } If you are dealing with 2-based numbers there is often a faster way. x % 2n == x & (2n - 1) // for n>0 At least for a positive divisor, the modulo operation can be replaced with a simple bitwise AND operation which allows for a much faster implementation of is odd. x % 2 == x & 1 x % 4 == x & 3 x % 8 == x & 7 Code bool is_odd(int n) { return n & 1 != 0; } Modular Multiplicative Inverse For two integers ‘a’ and ‘m’, the modular multiplicative inverse of ‘a’ under modulo ‘m’ is an ~ ‘x’ such that: integer a x = 1 (mod m) The value of x should be in {1, 2, … m-1}, i.e., in the range of integer modulo m. Note: x cannot be 0 as a*0 mod m will never be 1 The multiplicative inverse of “a modulo m” exists if and only if a and m are relatively prime (i.e., if gcd(a, m) = 1). Example: Input: a = 3, m = 11 Output: 4 Since (4*3) mod 11 = 1, 4 is modulo inverse of 3(under 11). Modular multiplicative inverse is an essential step in RSA public-key encryption method. Basic Approach Iterate through all numbers from 1 to m and for every number x, check if (a*x)%m is 1. Code int modInverse(int a, int m) { for (int x = 1; x < m; x++) if (((a%m) * (x%m)) % m == 1) return x; } Time Complexity: O(m) Optimized Approach 1 When m and a are coprime, use Extended Euclidean algorithms that takes two integers ‘a’ and ‘b’, finds their gcd and also find ‘x’ and ‘y’ such that: ax + by = gcd(a, b) To find the multiplicative inverse of ‘a’ under ‘m’, we put b = m in the above formula. Since we know that a and m are relatively prime, we can put the value of gcd as 1. ax + my = 1 If we take modulo m on both sides, we get ~ ax + my = 1 (mod m) We can remove the second term on the left side as ‘my (mod m)’ would always be 0 for an integer y. ~ 1 (mod m) ax = So, the ‘x’ that we can find using Extended Euclid Algorithm is the multiplicative inverse of ‘a’ Code int gcdExtended(int a, int b, int* x, int* y); // Function to find modulo inverse of a void modInverse(int a, int m) { int x, y; int g = gcdExtended(a, m, &x, &y); if (g != 1) cout << "Inverse doesn't exist"; else { // m is added to handle negative x int res = (x % m + m) % m; cout << "Modular multiplicative inverse is " << res; } } int gcdExtended(int a, int b, int* x, int* y) { if (a == 0) { *x = 0, *y = 1; return b; } int x1, y1; int gcd = gcdExtended(b % a, a, &x1, &y1); *x = y1 - (b / a) * x1; *y = x1; } return gcd; Optimized Approach 2 Iterative Implementation: Code // // // // Returns modulo inverse of a with respect to m using extended Euclid Algorithm Assumption: a and m are coprimes, i.e., gcd(a, m) = 1 int modInverse(int a, int m) { int m0 = m; int y = 0, x = 1; if (m == 1) return 0; while (a > 1) { // q is quotient int q = a / m; int t = m; // m is remainder now, process same as // Euclid's algo m = a % m, a = t; t = y; } // Update y and x y = x - q * y; x = t; // Make x positive if (x < 0) x += m0; } return x; Time Complexity: O(Log m) Optimized Approach 3 When m is prime, use Fermat’s little theorem to find the inverse. ~ am-1 = 1 (mod m) If we multiply both sides with a-1, we get a-1 ~ = am-2 (mod m) Code // To find GCD of a and b int gcd(int a, int b); // To compute x raised to power y under modulo m int power(int x, unsigned int y, unsigned int m); // Function to find modular inverse of a under modulo m // Assumption: m is prime void modInverse(int a, int m) { int g = gcd(a, m); if (g != 1) cout << "Inverse doesn't exist"; else { // If a and m are relatively prime, then modulo // inverse is a^(m-2) mode m cout << "Modular multiplicative inverse is " << power(a, m - 2, m); } } // To compute x^y under modulo m int power(int x, unsigned int y, unsigned int m) { if (y == 0) return 1; int p = power(x, y / 2, m) % m; p = (p * p) % m; return (y % 2 == 0) ? p : (x * p) % m; } int gcd(int a, int b) { if (a == 0) return b; return gcd(b % a, a); } Time Complexity: O(Log m) Binary Exponentiation Binary exponentiation (also known as exponentiation by squaring) is a trick which allows one to calculate an using only O(logn) multiplications. It can be used with any operations that have the property of associativity: (X Y) Z=X (Y Z) This applies to modular multiplication, to multiplication of matrices and to other problems. Algorithm: Raising a to the power of n is expressed naively as multiplication by a done n−1 times: an =a a … a. However, this approach is not practical for large a or n. 2 ab+c=ab.ac and a2b=ab.ab=(ab) The idea of binary exponentiation is that we split the work using the binary representation of the exponent. Recursion Approach an=1 if n==0 an=(an/2)2 if n>0 and n is even an =(a(n-1)/2)2 if n>0 and n is odd Code long long binpow(long long a, long long b) { if (b == 0) return 1; long long res = binpow(a, b / 2); if (b % 2) return res * res * a; else return res * res; } Time Complexity: O(Log n) We have to compute log n powers of a, and then have to do at most log n multiplications to get the final answer from them. No Recursion It computes all the powers in a loop, and multiplies the ones with the corresponding set bit in n. Although the complexity of both approaches is identical, this approach will be faster in practice since we have the overhead of the recursive calls. Code long long binpow(long long a, long long b) { long long res = 1; while (b > 0) { if (b & 1) res = res * a; a = a * a; b >>= 1; } return res; } Euler’s Totient Function Euler’s Totient function Φ (n) for an input n is the count of numbers in {1, 2, 3, …, n} that are relatively prime to n, i.e., the numbers whose GCD (Greatest Common Divisor) with n is 1. Example: Φ(1) = 1 gcd(1, 1) is 1 Φ(2) = 1 gcd(1, 2) is 1, but gcd(2, 2) is 2. Φ(3) = 2 gcd(1, 3) is 1 and gcd(2, 3) is 1 Some Important Facts to Know: 1) For a prime number p, Φ(p) is p-1 because gcd of all numbers from 1 to p-1 will be 1 2) For two numbers a and b, if gcd(a, b) is 1, then Φ(ab) = Φ(a) * Φ(b). 3) For any two prime numbers p and q, Φ(pq) = (p-1)*(q-1). This property is used in the RSA algorithm. 4) If p is a prime number, then Φ(pk) = pk – pk-1. This can be proved using Euler’s product formula. 5) Sum of values of totient functions of all divisors of n is equal to n. (d) = n d|n 6) Euler’s theorem: The theorem states that if n and a are coprime (Or relatively prime) positive integers, then aΦ(n) Φ 1 (mod n) The RSA cryptosystem is based on this theorem: In the particular case when m is prime say p, Euler’s theorem turns into the so-called Fermat’s little theorem: ap-1 Φ 1 (mod p) Compute Φ(n) for an input n Basic Approach Iterate through all numbers from 1 to n-1 and count numbers with gcd with n as 1. Code int gcd(int a, int b) { if (a == 0) return b; return gcd(b % a, a); } // A simple method to evaluate Euler Totient Function int phi(unsigned int n) { unsigned int result = 1; for (int i = 2; i < n; i++) if (gcd(i, n) == 1) result++; return result; } Time Complexity: The above code calls gcd function O(n) times. The time complexity of the gcd function is O(h) where “h” is the number of digits in a smaller number of given two numbers. Therefore, an upper bound on the time complexity of the above solution is O(nLogn) Optimized Approach 1 The idea is based on Euler’s product formula which states that the value of totient functions is below the product overall prime factors p of n. Φ(n) = n (1 - 1/p) The formula basically says that the value of Φ(n) is equal to n multiplied by the product of (1 – 1/p) for all prime factors p of n. For example value of Φ(6) = 6 * (1-1/2) * (1 – 1/3) = 2. Algorithm: 1) Initialize: result = n 2) Run a loop from 'p' = 2 to sqrt(n), do the following for every 'p'. a) If p divides n, then Set: result = result * (1.0 - (1.0 / (float) p)); Divide all occurrences of p in n. 3) Return result Code int phi(int n) { // Initialize result as n float result = n; // Consider all prime factors of n // and for every prime factor p, // multiply result with (1 - 1/p) for(int p = 2; p * p <= n; ++p) { // Check if p is a prime factor. if (n % p == 0) { // If yes, then update n and result while (n % p == 0) n /= p; } } result *= (1.0 - (1.0 / (float)p)); } // If n has a prime factor greater than sqrt(n) // (There can be at-most one such prime factor) if (n > 1) result *= (1.0 - (1.0 / (float)n)); return (int)result; Optimized Approach 2 Count all prime factors and their multiples and subtract this count from n to get the totient function value (Prime factors and multiples of prime factors won’t have gcd as 1) Algorithm: 1) Initialize result as n 2) Consider every number 'p' (where 'p' varies from 2 to Φn). If p divides n, then do following a) Subtract all multiples of p from 1 to n [all multiples of p will have gcd more than 1 (at least p) with n] b) Update n by repeatedly dividing it by p. 3) If the reduced n is more than 1, then remove all multiples of n from the result. Code int phi(int n) { // Initialize result as n int result = n; Explanation: Let n = 10. Initialize: result = 10 2 is a prime factor, so n = n/i = 5, result = 5 3 is not a prime factor. The for loop stops after 3 as 4*4 is not less than or equal to 10. After for loop, result = 5, n = 5 Since n > 1, result = result - result/n = 4 Fermat’s Little Theorem Fermat’s little theorem states that if p is a prime number, then for any integer a, the number a p – a is an integer multiple of p where p is a prime number. - a (mod p) ap = Special Case: If a is not divisible by p, Fermat’s little theorem is equivalent to the statement that ap-1 - 1 is an integer multiple of p. - 1 (mod p) a p-1 = OR -1 ap-1 % p = Note: a is not divisible by p. Fermats’s little theorem is used to find the inverse if we know m is prime. - 1 (mod m) a m-1 = If we multiply both sides with a-1, we get - am-2(mod m) a-1= Example: P = an integer Prime number a = an integer which is not multiple of P - 17 Let a = 2 and P = According to Fermat's little theorem - 1 mod(17) 217-1= Probabilities Probability refers to the extent of occurrence of events. When an event occurs like throwing a ball, picking a card from the deck, etc.., then there must be some probability associated with that event. In terms of mathematics, probability refers to the ratio of wanted outcomes to the total number of possible outcomes. There are three approaches to the theory of probability, namely: Empirical Approach Classical Approach Axiomatic Approach Probability of an Event If there are total p possible outcomes associated with a random experiment and q of them are favorable outcomes to the event A, then the probability of event A is denoted by P(A) and is given by P(A) = q/p The probability of non-occurrence of event A, i.e., P(A’) = 1 – P(A) Note: If the value of P(A) = 1, then event A is called a sure event. If the value of P(A) = 0, then event A is called an impossible event. Also, P(A) + P(A’) = 1 Theorems: General – Let A, B, C are the events associated with a random experiment, then P(AUB) = P(A) + P(B) – P(A B) P(AUB) = P(A) + P(B) if A and B are mutually exclusive P(AUBUC) = P(A) + P(B) + P(C) – P(A B) – P(B C)- P(C A) + P(A B C) P(AUB’) = P(A) – P(A B) P(A’UB) = P(B) – P(A B) Extension of Multiplication Theorem – Let A1, A2, ….., An are n events associated with a random experiment, then P(A1 A2 A3 ….. An) = P(A1)P(A2/A1)P(A3/A2 A1) ….. P(An/A1 A2 A3 ….. An-1) U U U U U U U U U U U U U U for (int p = 2; p * p <= n; p++) { // If prime[p] is not changed,it is a prime if (prime[p] == true) { // Update all multiples of p greater than or // equal to the square of it // numbers which are divisible by p // and are less than p^2 // are already marked. for (int i = p * p; i <= n; i += p) prime[i] = false; } } } // Print all prime numbers for (int p = 2; p <= n; p++) if (prime[p]) cout << p << " "; Time complexity: O(n*log(log(n))) Prime Factorization Prime factor is a prime number which is factor of the given number. You multiply prime factors together to get the given number. This is called prime factorization. Hence Prime Factorization is a way of expressing a number as a product of its prime factors. For Example: The prime factors of 28 are 2 and 7. And Prime Factorization of 28 is 2*2*7. 28 14 2 2 7 Prime factors ; 28 = 2, 2, 7 Some Important Facts to Know: 1. There exists only one set of prime factors for any number. Code // To compute x raised to power y under modulo m int power(int x, unsigned int y, unsigned int m); // Function to find modular inverse of a under modulo m // Assumption: m is prime void modInverse(int a, int m) { if (__gcd(a, m) != 1) cout << "Inverse doesn't exist"; else { } // If a and m are relatively prime, then // modulo inverse is a^(m-2) mode m cout << "Modular multiplicative inverse is " << power(a, m - 2, m); } // To compute x^y under modulo m int power(int x, unsigned int y, unsigned int m) { if (y == 0) return 1; int p = power(x, y / 2, m) % m; p = (p * p) % m; } return (y % 2 == 0) ? p : (x * p) % m; Permutation and Combination Permutation It is the different arrangements of a given number of elements taken one by one, or some, or all at a time. For example, if we have two elements A and B, then there are two possible arrangements, AB and BA. Number of permutations when ‘r’ elements are arranged out of a total of ‘n’ elements is nPr = n! / (n – r)! For example, let n = 4 (A, B, C and D) and r = 2 (All permutations of size 2). The answer is 4!/(4-2)! = 12. The twelve permutations are AB, AC, AD, BA, BC, BD, CA, CB, CD, DA, DB and DC. Combination It is the different selections of a given number of elements taken one by one, or some, or all at a time. For example, if we have two elements A and B, then there is only one way to select two items, we select both of them. Number of combinations when ‘r’ elements are selected out of a total of ‘n’ elements is n C r = n! / (r !) x (n – r)! . For example, let n = 4 (A, B, C and D) and r = 2 (All combinations of size 2). The answer is 4! / ((4-2)! *2!) = 6. The six combinations are AB, AC, AD, BC, BD, CD. n C r = n C (n – r) Program to calculate value of nCr A binomial coefficient C(n, k) can be defined as the coefficient of X k in the expansion of n (1 + X). A binomial coefficient C(n, k) also gives the number of ways, disregarding order, that k objects can be chosen from among n objects; more formally, the number of k-element subsets (or k-combinations) of an n-element set. Given two numbers n and r, find value of nCr Examples: Input: n = 5, r = 2 Output: 10 The value of 5C2 is 10 Input: n = 3, r = 1 Output: 3 nCr =n! / (r! * n-r!) Code int fact(int n); int nCr(int n, int r) { return fact(n) / (fact(r) * fact(n - r)); } // Returns factorial of n int fact(int n) { int res = 1; for (int i = 2; i <= n; i++) res = res * i; return res; } Binomial Coefficient A binomial coefficient C(n, k) can be defined as the coefficient of x k in the expansion of n (1 + x). A binomial coefficient C(n, k) also gives the number of ways, disregarding order, that k objects can be chosen from among n objects more formally, the number of k-element subsets (or k-combinations) of a n-element set. The Problem Write a function that takes two parameters n and k and returns the value of Binomial Coefficient C (n, k). For example, your function should return 6 for n = 4 and k = 2, and it should return 10 for n = 5 and k = 2. Optimal Substructure The value of C (n, k) can be recursively calculated using the following standard formula for Binomial Coefficients. C(n, k) = C(n-1, k-1) + C(n-1, k) C(n, 0) = C(n, n) = 1 Code // Returns value of Binomial int binomialCoeff(int n, int { if (k > n) return 0; if (k == 0 || k == n) return 1; return binomialCoeff(n + binomialCoeff(n } Coefficient C(n, k) k) 1, k - 1) - 1, k); Overlapping Subproblems It should be noted that the above function computes the same subproblems again and again. See the following recursion tree for n = 5 and k = 2. The function C(3, 1) is called two times. For large values of n, there will be many common subproblems. Binomial Coefficients Recursion tree for C (5,2) Since the same subproblems are called again, this problem has the Overlapping Subproblems property. So the Binomial Coefficient problem has both properties (see this and this) of a dynamic programming problem. Like other typical Dynamic Programming (DP) problems, re-computations of the same subproblems can be avoided by constructing a temporary 2D-array C [][] in a bottom-up manner. Code // A Dynamic Programming based solution that uses // table C[][] to calculate the Binomial Coefficient int min(int a, int b); // Returns value of Binomial Coefficient C(n, k) int binomialCoeff(int n, int k) { int C[n + 1][k + 1]; int i, j; // Calculate value of Binomial Coefficient // in bottom up manner for (i = 0; i <= n; i++) { for (j = 0; j <= min(i, k); j++) { // Base Cases if (j == 0 || j == i) C[i][j] = 1; // Calculate value using previously // stored values else C[i][j] = C[i - 1][j - 1] + C[i - 1][j]; } } return C[n][k]; } // A utility function to return // minimum of two integers int min(int a, int b) { return (a < b) ? a : b; } Time Complexity: O(n*k) Space Complexity: O(n*k) Optimized Code int binomialCoeff(int n, int k) { int C[k + 1]; memset(C, 0, sizeof(C)); C[0] = 1; // nC0 is 1 for (int i = 1; i <= n; i++) { // Compute next row of pascal triangle using // the previous row for (int j = min(i, k); j > 0; j--) C[j] = C[j] + C[j - 1]; } return C[k]; } Time Complexity: O(n*k) Space Complexity: O(k) Explanation 1========⟹> n = 0, C(0,0) = 1 1–1======⟹> n = 1, C(1,0) = 1, C(1,1) = 1 1–2–1====⟹> n = 2, C(2,0) = 1, C(2,1) = 2, C(2,2) = 1 1–3–3–1==⟹> n = 3, C(3,0) = 1, C(3,1) = 3, C(3,2) = 3, C(3,3)=1 1–4–6–4–1⟹> n = 4, C(4,0) = 1, C(4,1) = 4, C(4,2) = 6, C(4,3)=4, C(4,4)=1 So here every loop on i, builds i’th row of pascal triangle, using (i-1)th row At any time, every element of array C will have some value (ZERO or more) and in next iteration, value for those elements comes from the previous iteration. In statement, C[j] = C[j] + C[j-1] The right-hand side represents the value coming from the previous iteration (A row of Pascal’s triangle depends on the previous row). The left-Hand side represents the value of the current iteration which will be obtained by this statement. Let's say we want to calculate C(4, 3), i.e. n=4, k=3: All elements of array C of size 4 (k+1) are initialized to ZERO. i.e. C[0] = C[1] = C[2] = C[3] = C[4] = 0; Then C[0] is set to 1 For i = 1: C[1] = C[1] + C[0] = 0 + 1 = 1 ⟹> C(1,1) = 1 For i = 2: C[2] = C[2] + C[1] = 0 + 1 = 1 ⟹> C(2,2) = 1 C[1] = C[1] + C[0] = 1 + 1 = 2 ⟹> C(2,1) = 2 For i=3: C[3] = C[3] + C[2] = 0 + 1 = 1 ⟹> C(3,3) = 1 C[2] = C[2] + C[1] = 1 + 2 = 3 ⟹> C(3,2) = 3 C[1] = C[1] + C[0] = 2 + 1 = 3 ⟹> C(3,1) = 3 For i=4: C[4] = C[4] + C[3] = 0 + 1 = 1 ⟹> C(4,4) = 1 C[3] = C[3] + C[2] = 1 + 3 = 4 ⟹> C(4,3) = 4 C[2] = C[2] + C[1] = 3 + 3 = 6 ⟹> C(4,2) = 6 C[1] = C[1] + C[0] = 3 + 1 = 4 ⟹> C(4,1) = 4 C(4,3) = 4 is the answer in our example. Memoization Approach The idea is to create a lookup table and follow the recursive top-down approach. Before computing any value, we check if it is already in the lookup table. If yes, we return the value. Else we compute the value and store it in the lookup table. Optimized Code int binomialCoeffUtil(int n, int k, int** dp) { // If value in lookup table then return if (dp[n][k] != -1) // return dp[n][k]; // store value in a table before return if (k == 0) { dp[n][k] = 1; return dp[n][k]; } // store value in table before return if (k == n) { dp[n][k] = 1; return dp[n][k]; } } // save value in lookup table before return dp[n][k] = binomialCoeffUtil(n - 1, k - 1, dp) + binomialCoeffUtil(n - 1, k, dp); return dp[n][k]; int binomialCoeff(int n, int k) { int** dp; // make a temporary lookup table dp = new int*[n + 1]; // loop to create table dynamically for (int i = 0; i < (n + 1); i++) { dp[i] = new int[k + 1]; } } // nested loop to initialise the table with -1 for (int i = 0; i < (n + 1); i++) { for (int j = 0; j < (k + 1); j++) { dp[i][j] = -1; } } return binomialCoeffUtil(n, k, dp); Ways to sum to N using Natural Numbers up to K with repetitions allowed Given two integers N and K, the task is to find the total number of ways of representing N as the sum of positive integers in the range [1, K], where each integer can be chosen multiple times. Examples: Input: N = 8, K = 2 Output: 5 Explanation: All possible ways of representing N as sum of positive integers less than or equal to K are: {1, 1, 1, 1, 1, 1, 1, 1}, the sum is 8. {2, 1, 1, 1, 1, 1, 1}, the sum is 8. {2, 2, 1, 1, 1, 1}, the sum is 8. 2, 2, 2, 1, 1}, the sum is 8. {2, 2, 2, 2}}, the sum is 8. Therefore, the total number of ways is 5. Naive Approach: The simplest approach to solve the given problem is to generate all possible combinations of choosing integers over the range [1, K] and count those combinations whose sum is N. Time Complexity: O(KN)t Space Complexity: O(1) Optimized Approach: The above approach has Overlapping Subproblems and an Optimal Substructure. Hence, in order to optimize, Dynamic Programming is needed to be performed based on the following observations: Considering dp[i] stores the total number of ways for representing i as the sum of integers lying in the range [1, K], then the transition of states can be defined as: For i in the range [1, K] and for every j in the range [1, N] The value of dp[j] is equal to (dp[j]+ dp[j – i]), for all j ≥ i. Follow the steps below to solve the problem: Initialize an array, say dp[], with all elements as 0, to store all the recursive states. Initialize dp[0] as 1. Now, iterate over the range [1, K] using a variable i and perform the following steps: Iterate over the range [1, N], using a variable j, and update the value of dp[j] as dp[j]+ dp[j – i], if j ≥ i. After completing the above steps, print the value of dp[N] as the result. Code int NumberOfways(int N, int K) { // Initialize a list vector<int> dp(N + 1, 0); // Update dp[0] to 1 dp[0] = 1; // Iterate over the range [1, K + 1] for (int row = 1; row < K + 1; row++) { // Iterate over the range [1, N + 1] for (int col = 1; col < N + 1; col++) { // If col is greater // than or equal to row if (col >= row) // Update current // dp[col] state dp[col] = dp[col] + dp[col - row]; } } } return(dp[N]); Time Complexity: O(N * K) Space Complexity: O(N) Count ways to place M objects in distinct partitions of N boxes Given two positive integers N and M, the task is to find the number of ways to place M distinct objects in partitions of even indexed boxes which are numbered [1, N] sequentially, and every ith Box has a distinct partition. Since the answer can be very large, print modulo 1000000007. Examples: Input: N = 2, M = 1 Output: 2 Explanation: Since, N = 2. There is only one even indexed box i.e box 2, having 2 partitions. Therefore, there are two positions to place an object. Therefore, the number of ways = 2. Approach: Follow the steps below to solve the problem: M objects are to be placed in even indexed box’s partitions. Let S be the total even indexed box’s partitions in N boxes. The number of partitions is equal to the summation of all even numbers up to N. Therefore, Sum S = X * (X + 1), where X = floor(N / 2). Each object can occupy one of S different positions. Therefore, the total number of ways = S*S*S…(M times) = SM. Code const int MOD = 1000000007; // Iterative Function to calculate // (x^y)%p in O(log y) int power(int x, unsigned int y, int p = MOD) { // Initialize Result int res = 1; // Update x if x >= MOD // to avoid multiplication overflow x = x % p; while (y > 0) { // If y is odd, multiply x with result if (y & 1) res = (res * 1LL * x) % p; // // // // multiplied by long long int, to avoid overflow becauuse res * x <= 1e18, which is out of bounds for integer // n must be even now // y = y/2 y = y >> 1; // Change x to x^2 x = (x * 1LL * x) % p; } } return res; // Utility function to find // the Total Number of Ways void totalWays(int N, int M) { // Number of Even Indexed // Boxes int X = N / 2; // Number of paritions of // Even Indexed Boxes int S = (X * 1LL * (X + 1)) % MOD; } // Number of ways to distribute // objects cout << power(S, M, MOD) << "\n"; Time Complexity: O(log M) Space Complexity: O(1) Count all unique outcomes possible by performing S ﬂips on N coins Given two positive integers N and S, the task is to count the number of unique outcomes possible when S flip operations are performed on N coins. Examples: Input: N = 3, S = 4 Output: 3 Explanation: Considering the initial configuration of coins to be “HHH”, then the possible combinations of 4 flips are: Flipping the 1st and 2nd coins once and the third coin twice modifies the configuration to “TTH”. Flipping the 1st and 3rd coins once and the 2nd coin twice modifies the configuration to “THT”. Flipping the 2nd and 3rd coins once and the 1st coin twice modifies the configuration to “HTT”. The above three combinations are unique. Therefore, the total count is 3. Input: N = 3, S = 6 Output: 4 Consider F(N, S) represents the number of unique outcomes when N coins are tossed with the total number of flips equals to S. Then F(N, S) can also be expressed as the sum of all combinations with 1 flip or 2 flips i.e., F(N, S) = F(N – 1, S – 1) + F(N – 1, S – 2) The base case for this recurrence relation is F(K, K) whose value is 1 for all (K > 1). Below is the table that shows the distribution of F(N, S) = F(N – 1, S – 1) + F(N – 1, S – 2), where F(K, K) = 1. Algorithm Declare a function, say numberOfUniqueOutcomes(N, S) that takes the number of coins and flips allowed as the parameters respectively and perform the following steps: If the value of S is less than N, then return 0 from the function. If the value of N is S or 1, then return 1 from the function as this is one of the unique combinations. Recursively return summation of the two recursive states as: return numberOfUniqueOutcomes(N – 1, S – 1) + numberOfUniqueOutcomes(N – 1, S – 2) After completing the above steps, print the value returned by the function numberOfUniqueOutcomes(N, S) as the resultant number of outcomes. Below is the implementation of the above approach: // Function to recursively count the // number of unique outcomes possible // S flips are performed on N coins int numberOfUniqueOutcomes(int N, int S) { // Base Cases if (S < N) return 0; if (N == 1 || N == S) return 1; } // Recursive Calls return (numberOfUniqueOutcomes(N - 1, S - 1) + numberOfUniqueOutcomes(N - 1, S - 2)); Time Complexity: O(2N) Space Complexity: O(N) Theorems: The above approach can also be optimized by storing the recursive states as it contains overlapping subproblems. Therefore, the idea is to use memoization to store the repeated states. Follow the steps below to solve the problem: Initialize a 2D array, say dp[][] of dimensions N*M such that dp[i][j] stores the number of possible outcomes using i coins and j number of flips. Declare a function, say numberOfUniqueOutcomes(N, S), that takes the number of coins and flips allowed as the parameters respectively and perform the following steps: If the value of S is less than N, then update the value of dp[N][S] as 0 and return this value from the function. If the value of N is S or 1, then update the value of dp[N][S] as 1 and return this value from the function as this is one of the unique combinations. If the value of dp[N][S] is already calculated, then return the value dp[N][S] from the function. Recursively update the value of dp[N][S] summation of the two recursive states as shown below and return this value from the function. dp[N][S] = numberOfUniqueOutcomes(N – 1, S – 1) + numberOfUniqueOutcomes(N – 1, S – 2) After completing the above steps, print the value returned by the function numberOfUniqueOutcomes(N, S) as the resultant number of outcomes. Code // Dimensions of the DP table #define size 1001 // Stores the dp states int ans[size][size] = { 0 }; // Function to recursively count the // number of unique outcomes possible // by performing S flips on N coins int numberOfUniqueOutcomes(int n, int s) { // Base Case if (s < n) ans[n][s] = 0; else if (n == 1 || n == s) ans[n][s] = 1; // If the count for the current // state is not calculated, then // calculate it recursively else if (!ans[n][s]) { ans[n][s] = numberOfUniqueOutcomes(n s + numberOfUniqueOutcomes(n s } } // Otherwise return the // already calculated value return ans[n][s]; Time Complexity: O(N*S) Space Complexity: O(N*S) 1, 1) - 1, - 2); Number System Electronic and Digital systems may use a variety of different number systems, (e.g. Decimal, Hexadecimal, Octal, Binary). A number N in base or radix b can be written as: (N)b = dn-1 dn-2 — — — — d1 d0 . d-1 d-2 — — — — d-m In the above, dn-1 to d0 is the integer part, then follows a radix point, and then d-1 to d-m is the fractional part. dn-1 = Most significant bit (MSB) d-m = Least significant bit (LSB) How to convert a number from one base to another? 1. Decimal to Binary Algorithm: Store the remainder when the number is divided by 2 in an array. Divide the number by 2 Repeat the above two steps until the number is greater than zero. Print the array in reverse order now. Example: If the binary number is 10. Remainder when 10 is divided by 2 is zero. Therefore, arr[0] = 0. Divide 10 by 2. New number is 10/2 = 5. Remainder when 5 is divided by 2 is 1. Therefore, arr[1] = 1. Divide 5 by 2. New number is 5/2 = 2. Remainder when 2 is divided by 2 is zero. Therefore, arr[2] = 0. Divide 2 by 2. New number is 2/2 = 1. Remainder when 1 is divided by 2 is 1. Therefore, arr[3] = 1. Divide 1 by 2. New number is 1/2 = 0. Since the number becomes = 0. Print the array in reverse order. Therefore, the equivalent binary number is 1010. Note: Keep multiplying the fractional part with 2 until decimal part 0.00 is obtained. (0.25)10 = (0.01)2 Answer: (10.25)10 = (1010.01)2 Code void decToBinary(int n) { // array to store binary number int binaryNum[32]; // counter for binary array int i = 0; while (n > 0) { } } // storing remainder in binary array binaryNum[i] = n % 2; n = n / 2; i++; // printing binary array in reverse order for (int j = i - 1; j >= 0; j--) cout << binaryNum[j]; 2. Binary to Decimal Example: (1010.01)2 1×23 + 0×22 + 1×21+ 0×20 + 0×2 -1 + 1×2 -2 = 8+0+2+0+0+0.25 = 10.25 (1010.01)2 = (10.25)10 Algorithm: Extract the digits of a given binary number starting from the rightmost digit Keep a variable dec_value. At the time of extracting digits from the binary number, multiply the digit with the proper base Add it to the variable dec_value. variable dec_value will store the required decimal number. Example: If the binary number is 111. dec_value = 1*(2 ) + 1*(2 ) + 1*(2 ) = 7 2 1 0 Code int binaryToDecimal(int n) { int num = n; int dec_value = 0; // Initializing base value to 1, i.e 2^0 int base = 1; int temp = num; while (temp) { int last_digit = temp % 10; temp = temp / 10; dec_value += last_digit * base; } } base = base * 2; return dec_value; 3. Decimal to Octal Let’s start with an example (10)10 = (12)8 Algorithm: Store the remainder when the number is divided by 8 in an array. Divide the number by 8 now Repeat the above two steps until the number is not equal to 0. Print the array in reverse order now. Example: If the given decimal number is 16. Remainder when 16 is divided by 8 is 0. Therefore, arr[0] = 0. Divide 16 by 8. New number is 16/8 = 2. Remainder, when 2 is divided by 8, is 2. Therefore, arr[1] = 2. Divide 2 by 8. New number is 2/8 = 0. Since the number becomes = 0. Code void DecimalToOctal(int decimalNum) { int octalNum = 0, placeValue = 1; int dNo = decimalNum; while (decimalNum != 0) { octalNum += (decimalNum % 8) * placeValue; decimalNum /= 8; placeValue *= 10; } cout<<"Octal form of decimal number "<<dNo<<" is "<<octalNum<<endl; } 4. Octal to Decimal Let’s take an Example: 67(8) = 55(10) Algorithm: Extract the digits of a given octal number starting from the rightmost digit Keep a variable dec_value. At the time of extracting digits from the octal number, multiply the digit with the proper base (Power of 8) Add it to the variable dec_value. The variable dec_value will store the required decimal number. Example: If the octal number is 67. dec_value = 6*(81) + 7*(80) = 55 Code int octalToDecimal(int n) { int num = n; int dec_value = 0; // Initializing base value to 1, i.e 8^0 int base = 1; int temp = num; while (temp) { // Extracting last digit int last_digit = temp % 10; temp = temp / 10; // Multiplying last digit with appropriate // base value and adding it to dec_value dec_value += last_digit * base; base = base * 8; } return dec_value; } 5. Hexadecimal and Binary Example: Input: Hexadecimal = 1AC5 Output: Binary = 0001101011000101 Explanation: Equivalent binary value of 1: 0001 Equivalent binary value of A: 1010 Equivalent binary value of C: 1100 Equivalent binary value of 5: 0101 Algorithm: A hexadecimal number is a positional numeral system with a radix, or base, of 16 and uses sixteen distinct symbols. A binary number is a number expressed in the base-2 binary numeral system, which uses only two symbols: which are 0 (zero) and 1 (one). To convert a HexaDecimal number to Binary, the binary equivalent of each digit of the HexaDecimal number is evaluated and combined at the end to get the equivalent binary number. Code void HexToBin(string hexdec) { long int i = 0; while (hexdec[i]) { } } switch (hexdec[i]) { case '0': cout << "0000"; break; case '1': cout << "0001"; break; case '2': cout << "0010"; break; case '3': cout << "0011"; break; case '4': cout << "0100"; break; case '5': cout << "0101"; break; case '6': cout << "0110"; break; case '7': cout << "0111"; break; case '8': cout << "1000"; break; case '9': cout << "1001"; break; case 'A': case 'a': cout << "1010"; break; case 'B': case 'b': cout << "1011"; break; case 'C': case 'c': cout << "1100"; break; case 'D': case 'd': cout << "1101"; break; case 'E': case 'e': cout << "1110"; break; case 'F': case 'f': cout << "1111"; break; default: cout << "\nInvalid hexadecimal digit " << hexdec[i]; } i++; Linear Diophantine Equations A Diophantine equation is a polynomial equation, usually in two or more unknowns, such that only the integral solutions are required. An Integral solution is a solution such that all the unknown variables take only integer values. Given three integers a, b, c representing a linear equation of the form: ax + by = c. We have to determine if the equation has a solution such that x and y are both integral values. For example if a = 3, b = 6, c = 9 then there is a solution possible. The Equation turns out to be, 3x + 6y = 9 as one integral solution would be x = 1 , y = 1. This Diophantine equation has a solution (where x and y are integers) if and only if c is a multiple of the greatest common divisor of a and b. Moreover, if (x, y) is a solution, then the other solutions have the form (x + kv, y − ku), where k is an arbitrary integer, and u and v are the quotients of a and b (respectively) by the greatest common divisor of a and b. Algorithm: For linear Diophantine equation equations, integral solutions exist if and only if, the GCD of coefficients of the two variables divides the constant term perfectly. In other words, the integral solution exists if GCD(a,b) divides c. Thus, the algorithm to determine if an equation has an integral solution is pretty straightforward. Find GCD of a and b Check if c % GCD(a ,b) ==0 If yes then print Possible Else print Not Possible Code int gcd(int a, int b) { return (a%b == 0)? abs(b) : gcd(b,a%b); } bool isPossible(int a, int b, int c) { return (c%gcd(a,b) == 0); } Time Complexity: O(log n), where n is the maximum of a and b