# Maths 1: Modular Arithmetic & GCD
---
## Modular Arithmetic Introduction
A % B = Remainder when A is divided by B
Range of A % B will be within **[0, B - 1]**
### Why do we need Modular Arithmetic(%) ?
The most useful need of `%` is to limit the range of data. We don't have unlimited range in **Integer** OR **Long**, hence after a certain limit, we cannot store data. In such cases, we can apply mod to limit the range.
### Rules for applying mod on Arithmetic Operations
**1.** `(a + b) % m = (a % m + b % m) % m`
**Example:**
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/401/original/Screenshot_2023-10-19_at_11.25.07_AM.png?1697694922" />
---
**2.** `(a * b) % m = (a % m * b % m) % m`
**3.** `(a + m) % m = (a % m + m % m) % m = (a % m) % m = a % m`
---
**4.** `(a - b) % m = (a % m - b % m + m) % m`
This extra **m** term is added to ensure that the result remains within the range of **0 to m-1** even if the intermediate result of **(a % m - b % m) is negative**. This guarantees that the final remainder is a non-negative value.
Example:
Let's take **a = 7**, **b = 10**, and **m = 5**.
$(a - b)~ \%~ m ~=~ (7 - 10) ~\%~ 5 ~=~ -3 ~\%~ 5 = -3$ (which is not in the range 0 to 4)
Now we can simly do
(-3 + 5) % 5 = 2 (now value is in the range 0 to 4)
---
**5.** `(a ^ b) % m = ( (a % m) ^ b ) % m`
(a raise to power b)
---
### Question
Evaluate :
$(37^{103} - 1) \% 12$
**Choices**
- [ ] 1
- [x] 0
- [ ] No Idea
- [ ] 10
**Explanation**:
$(37^{103}-1)\%12$
$=>~ ((37^{103}~\%12)-(1\%12)+12)\%12$
$=>~ (((37\%12){103}~\%12)-1+12)~\%12$
$=>~ (1-1+12)\%12 = 12\%12 =0$
---
### Question
What is the result of the following modular arithmetic operation?
(25+13)%7
**Choices**
- [ ] 1
- [ ] 2
- [x] 6
- [ ] 4
**Explanation**
(25+13)%7=38%7=3⋅7+3=3
Therefore, the correct choice is 6.
---
### Question 1 Count pairs whose sum is a multiple of m
Given N array elements, find count of pairs (i, j) such that $(arr[i] + arr[j]) ~\%~ m = 0$
**Note:** $i~!=~j$ and pair(i, j) is same as pair(j, i)
**Example**
`A[ ] = {4, 3, 6, 3, 8, 12}`
`m = 6`
Pairs that satisfy **$(arr[i] + arr[j]) ~\%~ 6 = 0$** are:
`(4 + 8) % 6 = 0`
`(3 + 3) % 6 = 0`
`(6 + 12) % 6 = 0`
:::warning
Please take some time to think about the solution approach on your own before reading further.....
:::
#### Brute Force Approach
* For each pair of distinct indices `i` and `j` (where `i != j`), the sum $arr[i] ~+~ arr[j]$ is calculated, and then the remainder of this sum when divided by `m` is checked.
* If the remainder is 0, then the pair `(i, j)` satisfies the condition, and the count is incremented. This approach has a time complexity of $O(N^2)$, where N is the number of elements in the array, as it involves checking all possible pairs.
#### Optimal Approach
**Hint:**
We can utilise the property: $(a + b) \% m = (a \% m + b \% m) \% m$
Instead of directly checking for $(a+b)\%m$, we can check for $(a ~\%~ m ~+~ b \% m) \% m$
**Idea:**
* Iterate through the array and calculate `A[i] % m` for all values.
* Now, the sum of `A[i] % m` for two values should be divisible by m.
**Example**:
```java
A[ ] = {2, 3, 4, 8, 6, 15, 5, 12, 17, 7, 18}
m = 6
```
After doing mod with 6, we'll get
```java
{2, 3, 4, 2, 0, 3, 5, 0, 5, 1, 0}
Note: The range of A[i] % 6 is from 0 to 5
```
* Summing 1 with 5 will give sum divisible by 6.
* Likewise, 2 with 4, 3 with 3, and lastly 0 with 0.
#### Algorithm
* Iterate given array and calculate $A[i]\%m$.
* Create a frequency array of size **m** to store the frequency of remainders obtained from the elements.
* For each element, find the complement remainder needed for the sum to be divisible by `m`. Count frequency of complement remainder. Add these counts to get the total count of pairs satisfying the condition.
* **Note:** Mod 0 will form a pair with 0, i.e if m = 6, and say 12 & 18 are present in given array, doing 12 % 6 and 18 % 6 will result in 0.
#### Dry Run
```java
A[ ] = {2, 3, 4, 8, 6, 15, 5, 12, 17, 7, 18}
m = 6
```
After doing mod with 6, we'll get
```java
{2, 3, 4, 2, 0, 3, 5, 0, 5, 1, 0}
```
* We'll keep inserting frequency of elements in frequency array while iterating over remainder values -
| Remainder | Pair for it | Frequency | Count | Freq array |
|:---------:|:-------------------:|:----------------------------------------------------------------------:|:-----:| --- |
| 2 | $6-2 = 4$ | 4 is not yet present, but insert 2 for future use. | 0 | 2:1 |
| 3 | $6-3 = 3$ | 3 is not yet present, but insert 3 for future use. | 0 | 2:1 3:1 |
| 4 | $6-4 = 2$ | 2 is present with freq 1, count += 1 i.e, **1**; insert 4 for future use | 1 | 2:1 3:1 4:1 |
| 2 | $6-2 = 4$ | 4 is present with freq 1, count += 1 i.e, **2**; update frequency of 2. | 2 | 2:2 3:1 4:1 |
| 0 | 0 forms pair with 0 | 0 is not yet present, but insert 0 for future use. | 2 | 2:2 3:1 4:1 0:1 |
| 3 | $6-3 = 3$ | 3 is present with freq 1, count += 1 i.e, **3**; update frequency of 3. | 3 | 2:2 3:2 4:1 0:1 |
| 5 | $6-5 = 1$ | 1 is not yet present, but insert 5 for future use. | 3 | 2:2 3:2 4:1 0:1 5:1 |
| 0 | 0 forms pair with 0 | 0 is present with freq 1, count += 1 i.e, **4**; update frequency of 0. | 4 | 2:2 3:2 4:1 0:2 5:1 |
| 5 | $6-5 = 1$ | 1 is not yet present, but update frequency of 5 | 4 | 2:2 3:2 4:1 0:2 5:2 |
| 1 | $6-1 = 5$ | 5 is present with freq 2, count += 2 i.e, **6**; update frequency of 1. | 6 | 2:2 3:2 4:1 0:2 5:2 1:1 |
| 0 | 0 forms pair with 0 | 0 is present with freq 2, count += 2 i.e, **8**; update frequency of 0. | 8 | 2:2 3:2 4:1 0:3 5:2 1:1 |
#### Pseudocode
```cpp
int pairSumDivisibleByM(A, m) {
N = A.length;
freq[N] = {
0
};
count = 0;
for (int i = 0; i < N; i++) {
val = A[i] % m;
if (val == 0) {
pair = 0;
} else {
pair = m - val;
}
ans += freq[pair];
freq[val]++;
}
return count;
}
```
**Time Complexity** - `O(N)`
---
### Question
**Space Complexity**: Pair Sum Divisible by M
**Choices**
- [ ] O(N)
- [x] O(M)
- [ ] O(N+M)
**Explanation**
Space Complexity (SC) is `O(M)`, where M is the modulus value. This is because the frequency array of size M is required to store frequency of elements from 0 to M-1.
---
## GCD Basics
### Explanation
* GCD - Greatest Common Division
* HCF - Highest Common Factor
* GCD(A, B) - Greatest factor that divides both a and b
If we have `GCD(A, B) = x`
This implies:-
* A % x = 0
* B % x = 0
and hence `x` is the highest factor of both A and B
**Example - 1**
GCD(15, 25) = 5
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/487/original/Screenshot_2023-10-19_at_8.14.14_PM.png?1697726838" width="300"/>
**Example - 2**
GCD(12, 30) = 6
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/490/original/Screenshot_2023-10-19_at_8.14.22_PM.png?1697726994" width="250" />
**Example - 3**
GCD(0, 4) = 4
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/488/original/Screenshot_2023-10-19_at_8.14.32_PM.png?1697726867" width="250" />
**Example - 4**
GCD(0, 0) = Infinity
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/489/original/Screenshot_2023-10-19_at_8.14.39_PM.png?1697726934" width="250"/>
---
## Properties of GCD
### Property - 1
GCD(A, B) = GCD(B, A)
### Property - 2
GCD(0, A) = A
### Property - 3
GCD(A, B, C) = GCD(A, GCD(B, C)) = GCD(B, GCD(C, A)) = GCD(C, GCD(A, B))
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/491/original/Screenshot_2023-10-19_at_8.14.46_PM.png?1697727034" width="500"/>
### Property - 4
Given `A >= B > 0`,
**GCD(A, B) = GCD(A - B, B)**
**Example:**
<img src="https://hackmd.io/_uploads/S1yPWyBh3.png" width="500"/>
### Property - 5
GCD(A, B) = GCD(A % B, B)
---
### Question
gcd(0,8) = ?
Chose the correct answer
**Choices**
- [ ] 1
- [x] 8
- [ ] 0
- [ ] not defined
---
### Question
TC of gcd(a1,a2,a3,....,an) is:
Chose the correct answer
**Choices**
- [ ] O(log(max number))
- [ ] O(N)
- [x] O(N*log(max number)
- [ ] O(N^2)
---
### Question
Given an array A = [15, 21, 33, 45], find the GCD of all elements in the array.
**Choices**
- [ ] 4
- [x] 3
- [ ] 6
- [ ] 9
---
## Function of GCD
### Write a function to find GCD(A, B)
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/492/original/Screenshot_2023-10-19_at_8.15.12_PM.png?1697727112" width="600" />
Suppose we have two positive numbers a, b then:
```java
int gcd(a, b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
```
**Time Complexity(TC):** O(log(max(a, b)))
### Given an array, calculate GCD of the entire array
**Example:**
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/493/original/Screenshot_2023-10-19_at_8.15.19_PM.png?1697727148" width="500" />
```java
int gcdArr(int[] arr) {
int ans = arr[0];
int n = arr.length();
for (int i = 0; i < n; i++) {
ans = gcd(ans, arr[i])
}
return ans;
}
```
---
### Problem 2 Delete One
**Question**
Given arr[N] elements , we have to delete one element such that GCD of the remaining elements becomes maximum.
**Example:**
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/494/original/Screenshot_2023-10-19_at_8.15.30_PM.png?1697727189" width="600"/>
#### Brute Force Approach
The brute approach for this problem will be to delete arr[i], and then calculate the GCD for all the remaining elements. This will be repeated for all the elements.
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/495/original/Screenshot_2023-10-19_at_8.15.39_PM.png?1697727231" width="300" />
:::warning
Please take some time to think about the optimised approach on your own before reading further.....
:::
#### Optimal Approach: Prefix Array
**Approach:**
* Idea is to find the GCD value of all the sub-sequences of length (N – 1) and removing the element which is not present in the sub-sequence with that GCD. The maximum GCD found would be the answer.
* To find the GCD of the sub-sequences optimally, maintain a `prefixGCD[]` and a `suffixGCD[]` array using single state dynamic programming.
* The maximum value of GCD(`prefixGCD[i – 1]`, `suffixGCD[i + 1]`) is the required answer.
The implementation is given below:
```java
// Recursive function to return gcd of a and b
static int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
static int MaxGCD(int a[], int n) {
// Prefix and Suffix arrays
int Prefix[] = new int[n + 2];
int Suffix[] = new int[n + 2] ;
Prefix[1] = a[0];
for (int i = 2; i <= n; i += 1) {
Prefix[i] = gcd(Prefix[i - 1], a[i - 1]);
}
Suffix[n] = a[n - 1];
for (int i = n - 1; i >= 1; i -= 1) {
Suffix[i] = gcd(Suffix[i + 1], a[i - 1]);
}
// If first or last element of the array has to be removed
int ans = Math.max(Suffix[2], Prefix[n - 1]);
// If any other element is replaced
for (int i = 2; i < n; i += 1) {
ans = Math.max(ans, gcd(Prefix[i - 1], Suffix[i + 1]));
}
return ans;
}
```
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/496/original/Screenshot_2023-10-19_at_8.15.46_PM.png?1697727278" width="300" />
---
### Proof of gcd(a, b) = gcd(a-b, b)
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/437/original/Screenshot_2023-10-19_at_12.16.30_PM.png?1697698017" />