Uploaded by prabaltripathi171102

PROGRAMMING MATHS

advertisement
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 flips 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
Download