---
# System prepended metadata

title: 'Maths 1: Modular Arithmetic & GCD'

---

# 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" />