
- DSA - Home
- DSA - Overview
- DSA - Environment Setup
- DSA - Algorithms Basics
- DSA - Asymptotic Analysis
- Data Structures
- DSA - Data Structure Basics
- DSA - Data Structures and Types
- DSA - Array Data Structure
- DSA - Skip List Data Structure
- Linked Lists
- DSA - Linked List Data Structure
- DSA - Doubly Linked List Data Structure
- DSA - Circular Linked List Data Structure
- Stack & Queue
- DSA - Stack Data Structure
- DSA - Expression Parsing
- DSA - Queue Data Structure
- DSA - Circular Queue Data Structure
- DSA - Priority Queue Data Structure
- DSA - Deque Data Structure
- Searching Algorithms
- DSA - Searching Algorithms
- DSA - Linear Search Algorithm
- DSA - Binary Search Algorithm
- DSA - Interpolation Search
- DSA - Jump Search Algorithm
- DSA - Exponential Search
- DSA - Fibonacci Search
- DSA - Sublist Search
- DSA - Hash Table
- Sorting Algorithms
- DSA - Sorting Algorithms
- DSA - Bubble Sort Algorithm
- DSA - Insertion Sort Algorithm
- DSA - Selection Sort Algorithm
- DSA - Merge Sort Algorithm
- DSA - Shell Sort Algorithm
- DSA - Heap Sort Algorithm
- DSA - Bucket Sort Algorithm
- DSA - Counting Sort Algorithm
- DSA - Radix Sort Algorithm
- DSA - Quick Sort Algorithm
- Matrices Data Structure
- DSA - Matrices Data Structure
- DSA - Lup Decomposition In Matrices
- DSA - Lu Decomposition In Matrices
- Graph Data Structure
- DSA - Graph Data Structure
- DSA - Depth First Traversal
- DSA - Breadth First Traversal
- DSA - Spanning Tree
- DSA - Topological Sorting
- DSA - Strongly Connected Components
- DSA - Biconnected Components
- DSA - Augmenting Path
- DSA - Network Flow Problems
- DSA - Flow Networks In Data Structures
- DSA - Edmonds Blossom Algorithm
- DSA - Maxflow Mincut Theorem
- Tree Data Structure
- DSA - Tree Data Structure
- DSA - Tree Traversal
- DSA - Binary Search Tree
- DSA - AVL Tree
- DSA - Red Black Trees
- DSA - B Trees
- DSA - B+ Trees
- DSA - Splay Trees
- DSA - Range Queries
- DSA - Segment Trees
- DSA - Fenwick Tree
- DSA - Fusion Tree
- DSA - Hashed Array Tree
- DSA - K-Ary Tree
- DSA - Kd Trees
- DSA - Priority Search Tree Data Structure
- Recursion
- DSA - Recursion Algorithms
- DSA - Tower of Hanoi Using Recursion
- DSA - Fibonacci Series Using Recursion
- Divide and Conquer
- DSA - Divide and Conquer
- DSA - Max-Min Problem
- DSA - Strassen's Matrix Multiplication
- DSA - Karatsuba Algorithm
- Greedy Algorithms
- DSA - Greedy Algorithms
- DSA - Travelling Salesman Problem (Greedy Approach)
- DSA - Prim's Minimal Spanning Tree
- DSA - Kruskal's Minimal Spanning Tree
- DSA - Dijkstra's Shortest Path Algorithm
- DSA - Map Colouring Algorithm
- DSA - Fractional Knapsack Problem
- DSA - Job Sequencing with Deadline
- DSA - Optimal Merge Pattern Algorithm
- Dynamic Programming
- DSA - Dynamic Programming
- DSA - Matrix Chain Multiplication
- DSA - Floyd Warshall Algorithm
- DSA - 0-1 Knapsack Problem
- DSA - Longest Common Sub-sequence Algorithm
- DSA - Travelling Salesman Problem (Dynamic Approach)
- Hashing
- DSA - Hashing Data Structure
- DSA - Collision In Hashing
- Disjoint Set
- DSA - Disjoint Set
- DSA - Path Compression And Union By Rank
- Heap
- DSA - Heap Data Structure
- DSA - Binary Heap
- DSA - Binomial Heap
- DSA - Fibonacci Heap
- Tries Data Structure
- DSA - Tries
- DSA - Standard Tries
- DSA - Compressed Tries
- DSA - Suffix Tries
- Treaps
- DSA - Treaps Data Structure
- Bit Mask
- DSA - Bit Mask In Data Structures
- Bloom Filter
- DSA - Bloom Filter Data Structure
- Approximation Algorithms
- DSA - Approximation Algorithms
- DSA - Vertex Cover Algorithm
- DSA - Set Cover Problem
- DSA - Travelling Salesman Problem (Approximation Approach)
- Randomized Algorithms
- DSA - Randomized Algorithms
- DSA - Randomized Quick Sort Algorithm
- DSA - Karger’s Minimum Cut Algorithm
- DSA - Fisher-Yates Shuffle Algorithm
- Miscellaneous
- DSA - Infix to Postfix
- DSA - Bellmon Ford Shortest Path
- DSA - Maximum Bipartite Matching
- DSA Useful Resources
- DSA - Questions and Answers
- DSA - Selection Sort Interview Questions
- DSA - Merge Sort Interview Questions
- DSA - Insertion Sort Interview Questions
- DSA - Heap Sort Interview Questions
- DSA - Bubble Sort Interview Questions
- DSA - Bucket Sort Interview Questions
- DSA - Radix Sort Interview Questions
- DSA - Cycle Sort Interview Questions
- DSA - Quick Guide
- DSA - Useful Resources
- DSA - Discussion
DSA - Mathematical Algorithms
Mathematical algorithms are well-defined procedures that are used in solving mathematical problems related to data structures. We can see its application in competitive programming, data science and other complex mathematical concepts. These algorithms teach us how to solve a given problem by thinking logically and efficiently.
Important Mathematical Algorithms
Some of the important mathematical algorithms are −
Euclid's Algorithm
Sieve of Eratosthenes
Binary Exponentiation
Modular Arithmetic
Here is the detailed explanation −
Euclid's Algorithm
Euclid's algorithm, also referred to as the Euclidean algorithm, is a method that is used to compute GCD of two given integers. The term GCD is an abbreviation that stands for greatest common divisor. The GCD is also termed as highest common factor(HCF). It is defined as the largest integer that divides a given set of numbers.
Suppose, a given set of integers are A and B. Here's how the Euclidean Algorithm works to find their GCD −
If A is equal to 0 and B is the non-zero integer, then GCD(A, B) is B.
The GCD of two integers A and B remains unchanged on subtracting the smaller integer from the larger one. Therefore, repeating this process several times will result in GCD.
Recursively compute A mod B and when it becomes 0, we will get our GCD which is B.
Example
In the following example, we are going to illustrate how Euclid's algorithm works.
#include <stdio.h> int findGrtCmFact(int a, int b) { if (b == 0) { return a; } else { return findGrtCmFact(b, a % b); } } int main() { int valOne = 52; int valTwo = 28; printf("The GCD of %d and %d is: %d\n", valOne, valTwo, findGrtCmFact(valOne, valTwo)); return 0; }
#include <iostream> using namespace std; int findGrtCmFact(int a, int b) { if (b == 0) { return a; } else { return findGrtCmFact(b, a % b); } } int main() { int valOne = 52; int valTwo = 28; cout << "The GCD of " << valOne << " and " << valTwo << " is: " << findGrtCmFact(valOne, valTwo) << endl; return 0; }
public class GrtComFactr { public static int findGrtCmFact(int a, int b) { if (b == 0) { return a; } else { return findGrtCmFact(b, a % b); } } public static void main(String[] args) { int valOne = 52; int valTwo = 28; System.out.println("The GCD of " + valOne + " and " + valTwo + " is: " + findGrtCmFact(valOne, valTwo)); } }
def findGrtCmFact(a, b): if b == 0: return a else: return findGrtCmFact(b, a % b) valOne = 52 valTwo = 28 print("The GCD of {} and {} is: {}".format(valOne, valTwo, findGrtCmFact(valOne, valTwo)))
Output
The GCD of 52 and 28 is: 4
Sieve of Eratosthenes Algorithm
The Sieve of Eratosthenes algorithm is a way used for identifying prime numbers within a given range. The naive approach to finding prime numbers is by iterating over each number and checking whether the current number is prime or not. However, it is not the optimal solution.
Here's how the Sieve of Eratosthenes works −
Start with a list of numbers from 2 up to N. Initially, consider all these numbers as potential primes.
Begin with the first prime number, which is 2. Mark all multiples of 2 as composite. Move on to the next unmarked number in the list, which is the 3. Now, mark all multiples of 3 as composite.
After completing this process for all numbers up to n, we will be left with only the prime numbers that remain unmarked.
Example
The following example illustrates the working of Sieve of Eratosthenes algorithm.
#include <stdio.h> #include <stdbool.h> #include <string.h> // method to find primes void sieveOfEratos(int n) { // initially assuming all values are prime bool prm[n+1]; memset(prm, true, sizeof(prm)); // Loop over all numbers from 2 to sqrt(n) for(int currPrm = 2; currPrm*currPrm <= n; currPrm++) { // If current prime is still true, then it is a prime if(prm[currPrm] == true) { // Update all multiples of current prime for(int i = currPrm*currPrm; i <= n; i += currPrm) // Marking factor as not prime prm[i] = false; } } // to print the list of prime numbers printf("List of first 50 prime numbers: \n"); for(int i = 2; i <= n; i++) { if(prm[i] == true) printf("%d ", i); } } int main() { int lmt = 50; sieveOfEratos(lmt); return 0; }
#include <iostream> #include <vector> using namespace std; class SvEratos { public: // method to find primes static void sieveOfEratos(int n) { // initially assuming all values are prime vector<bool> prm(n+1, true); // Loop over all numbers from 2 to sqrt(n) for(int currPrm = 2; currPrm*currPrm <= n; currPrm++) { // If current prime is still true, then it is a prime if(prm[currPrm] == true) { // Update all multiples of current prime for(int i = currPrm*currPrm; i <= n; i += currPrm) // Marking factor as not prime prm[i] = false; } } // to print the list of prime numbers cout << "List of first 50 prime numbers: " << endl; for(int i = 2; i <= n; i++) { if(prm[i] == true) cout << i << " "; } cout << endl; } }; int main() { int lmt = 50; SvEratos::sieveOfEratos(lmt); return 0; }
public class SvEratos { // method to find primes public static void sieveOfEratos(int n) { // initially assuming all values are prime boolean prm[] = new boolean[n+1]; for(int i=0; i<=n; i++) prm[i] = true; // Loop over all numbers from 2 to sqrt(n) for(int currPrm = 2; currPrm*currPrm <=n; currPrm++) { // If current prime is still true, then it is a prime if(prm[currPrm] == true) { // Update all multiples of current prime for(int i = currPrm*currPrm; i <= n; i += currPrm) // Marking factor as not prime prm[i] = false; } } // to print the list of prime numbers System.out.println("List of first 50 prime numbers: "); for(int i = 2; i <= n; i++) { if(prm[i] == true) System.out.print(i + " "); } } public static void main(String[] args) { int lmt = 50; sieveOfEratos(lmt); } }
def sieveOfEratos(n): # initially assuming all values are prime prm = [True for _ in range(n+1)] # Loop over all numbers from 2 to sqrt(n) currPrm = 2 while currPrm * currPrm <= n: # If current prime is still true, then it is a prime if prm[currPrm] == True: # Update all multiples of current prime for i in range(currPrm * currPrm, n+1, currPrm): # Marking factor as not prime prm[i] = False currPrm += 1 # to print the list of prime numbers print("List of first 50 prime numbers: ") for i in range(2, n): if prm[i] == True: print(i, end=" ") # test the function lmt = 50 sieveOfEratos(lmt)
Output
List of first 50 prime numbers: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
Binary Exponentiation Algorithm
The binary exponentiation algorithm is a procedure used to calculate the power of a given number. The naive approach to solve this type of problem i.e. np is by multiplying the number with itself p-1 times. However, it is a time-consuming process and not an efficient process.
Instead of the above approach, we can use the binary exponentiation algorithm which works as follows −
When the power of any given number is 0, the result will be 1.
If the number itself is even, use the following equation: ((n2)p/2), where n is the number and p is the power of that given number.
If the given number is odd, use the equation: (n*(n(p-1)/2)2).
Example
In this example, we will show how Binary Exponentiation algorithm works.
#include <stdio.h> // function to calculate power int bnryExp(int bs, int ex) { int output = 1; while (ex > 0) { if (ex % 2 == 1) { output *= bs; } // Square of base bs *= bs; // Divide power by 2 ex >>= 1; } // Return the result stored in output return output; } int main() { int bs = 3; int ex = 6; // Print the result int result = bnryExp(bs, ex); printf("The output of %d to the power %d is: %d\n", bs, ex, result); return 0; }
#include <iostream> using namespace std; // method to calculate power int bnryExp(int bs, int ex) { // to store output int output = 1; while (ex > 0) { if (ex % 2 == 1) { output *= bs; } // Square of base bs *= bs; // Divide power by 2 ex /= 2; } // Return the result stored in output return output; } int main() { int bs = 3; int ex = 6; int result = bnryExp(bs, ex); // Print the result cout << "The output of " << bs << " to the power " << ex << " is: " << result << endl; return 0; }
public class Exponentiation { // method to calculate power public static int bnryExp(int bs, int ex) { // to store output int output = 1; while (ex > 0) { if (ex % 2 == 1) { output *= bs; } // Square of base bs *= bs; // Divide power by 2 ex /= 2; } // Return the result stored in output return output; } public static void main(String[] args) { int bs = 3; int ex = 6; // Print the result System.out.println("The output of " + bs + " to the power " + ex + " is: " + bnryExp(bs, ex)); } }
# method to calculate power def bnryExp(bs, ex): # to store output output = 1 while ex > 0: if ex % 2 == 1: output *= bs bs *= bs ex //= 2 return output bs = 3 ex = 6 result = bnryExp(bs, ex) print(f"The output of {bs} to the power {ex} is: {result}")
Output
The output of 3 to the power 6 is: 729
Modular Arithmetic
Modular arithmetic is a set of rules that is applicable in computing modulus expression. This concept becomes important when handling large numbers in competitive programming. The core idea of modular arithmetic is to find the remainder of a number upon division by another number.
Important properties of modulo are as follows −
(m mod n) mod n is equal to m mod n
(m*n) mod m is equal to 0
(P / Q) mod m ((P mod m) / (Q mod m)) mod m
(P + Q) mod m = ((P mod m) + (Q mod m)) mod m
(P Q) mod m = ((P mod m) (Q mod m) + m) mod m
(P * Q) mod m = ((P mod m) * (Q mod m)) mod m
Example
The following example demonstrates the working of modular arithmetic.
#include <stdio.h> // function to perform addition int modAddition(int valOne, int valTwo, int mod) { // to handle negative values if (valOne < 0) { valOne = (valOne % mod + mod) % mod; } if (valTwo < 0) { valTwo = (valTwo % mod + mod) % mod; } // addition int sum = (valOne + valTwo) % mod; // to ensure output is non-negative return (sum + mod) % mod; } int main() { int valOne = 22; int valTwo = 26; int mod = 5; int output = modAddition(valOne, valTwo, mod); printf("Modular addition of %d and %d modulo %d is: %d\n", valOne, valTwo, mod, output); return 0; }
#include <iostream> using namespace std; int modAddition(int valOne, int valTwo, int mod) { // to handle negative values if (valOne < 0) { valOne = (valOne % mod + mod) % mod; } if (valTwo < 0) { valTwo = (valTwo % mod + mod) % mod; } // addition int sum = (valOne + valTwo) % mod; // to ensure the result is non-negative return (sum + mod) % mod; } int main() { int valOne = 22; int valTwo = 26; int mod = 5; int output = modAddition(valOne, valTwo, mod); cout << "Modular addition of " << valOne << " and " << valTwo << " modulo " << mod << " is: " << output << endl; return 0; }
public class ModAdd { public static int modAddition(int valOne, int valTwo, int mod) { // to handle negative values if (valOne < 0) { valOne = (valOne % mod + mod) % mod; } if (valTwo < 0) { valTwo = (valTwo % mod + mod) % mod; } // addition int sum = (valOne + valTwo) % mod; // to ensure output is non-negative return (sum + mod) % mod; } public static void main(String[] args) { int valOne = 22; int valTwo = 26; int mod = 5; int output = modAddition(valOne, valTwo, mod); System.out.println("Modular addition of " + valOne + " and " + valTwo + " modulo " + mod + " is: " + output); } }
def mod_addition(val_one, val_two, mod): # to handle negative values if val_one < 0: val_one = (val_one % mod + mod) % mod if val_two < 0: val_two = (val_two % mod + mod) % mod # addition sum = (val_one + val_two) % mod # to ensure output is non-negative return (sum + mod) % mod val_one = 22 val_two = 26 mod = 5 output = mod_addition(val_one, val_two, mod) print(f"Modular addition of {val_one} and {val_two} modulo {mod} is: {output}")
Output
Modular addition of 22 and 26 modulo 5 is: 3