Open In App

Using Chinese Remainder Theorem to Combine Modular equations

Improve
Improve
Like Article
Like
Save
Share
Report

Given N modular equations: A ? x1mod(m1) . . A ? xnmod(mn) Find x in the equation A ? xmod(m1*m2*m3..*mn) where mi is prime, or a power of a prime, and i takes values from 1 to n. The input is given as two arrays, the first being an array containing values of each xi, and the second array containing the set of values of each prime. mi Output an integer for the value of x in the final equation. 

Examples : 

Consider the two equations
A ? 2mod(3)
A ? 3mod(5)
Input : 
2 3
3 5
Output : 
8

Consider the four equations,
A ? 3mod(4)
A ? 4mod(7)
A ? 1mod(9) (32)
A ? 0mod(11)
Input :
3 4 1 0
4 7 9 11
Output :
1243

Explanation : We aim to solve these equations two at a time. We take the first two equations, combine it, and use that result to combine with the third equation, and so on. The process of combining two equations is explained as follows, by taking example 2 for reference:

  1. A ? 3mod(4) and A ? 4mod(7) are the two equations that we are provided with at first. Let the resulting equation be some A0 ? x0 mod(m1 * m2).
    • A0 is given by m1‘ * m1 * x0 + m0‘ * m0 * x1 where m1‘ = modular inverse of m1 modulo m0 and m0‘ = modular inverse of m0 modulo m1
    • We can calculate modular inverse using extended euclidean algorithm.
    • We find x0 to be A0 mod (m1 * m2)
    • We get our new equation to be A ? 11mod(28), where A is 95
  2. We now try to combine this with equation 3, and by a similar method, we get A ? 235mod(252), where A = 2503
  3. And finally, on combining this with equation 4, we get A ? 1243mod(2772) where A = 59455 and x = 1243

We observe that 2772 is rightly equal to 4 * 7 * 9 * 11. We have thus found the value of x for the final equation. You can refer to Extended Euclidean Algorithm and Modular multiplicative inverse for extra information on these topics. 

C++




// C++ program to combine modular equations
// using Chinese Remainder Theorem
#include<bits/stdc++.h>
using namespace std;
 
// function that implements Extended euclidean
// algorithm
vector<int> extended_euclidean(int a,int b){
    if(a == 0){
        vector<int> temp;
        temp.push_back(b);
        temp.push_back(0);
        temp.push_back(1);
        return temp;
    }
    else{
        vector<int> temp(3);
        temp= extended_euclidean(b % a, a);
        int g = temp[0];
        int y = temp[1];
        int x = temp[2];
        temp[0] = g;
        temp[1] = x - ((b/a) * y);
        temp[2] = y;
        return temp;
    }
    vector<int> temp;
    return temp;
}
 
// modular inverse driver function
int modinv(int a,int m){
    vector<int> temp(3);
    temp = extended_euclidean(a, m);
    int g = temp[0];
    int x = temp[1];
    int y = temp[2];
     
    // Since we are taking the modulo of
    // negative numbers so, to have positive
    // output of the modulo we use this formula.
    int ans = x -  (floor(x/(float)m) * m);
    return ans;
}
     
 
// function implementing Chinese remainder theorem
// list m contains all the modulii
// list x contains the remainders of the equations
int crt(vector<int> &m,vector<int> & x)
{
   
    // We run this loop while the list of
    // remainders has length greater than 1
    while(1)
    {
       
        // temp1 will contain the new value
        // of A. which is calculated according
        // to the equation m1' * m1 * x0 + m0'
        // * m0 * x1
        int var1 = (modinv(m[1],m[0]));
        int var2 = (modinv(m[0],m[1]) );
        // cout << var1 << " " << var2 << endl;
        int temp1 = (modinv(m[1],m[0])) * x[0] * m[1] + (modinv(m[0],m[1]) )* x[1] * m[0];
 
        // temp2 contains the value of the modulus
        // in the new equation, which will be the
        // product of the modulii of the two
        // equations that we are combining
        int temp2 = m[0] * m[1];
        // cout << temp1<< " "<<temp2<< endl;
        // we then remove the first two elements
        // from the list of remainders, and replace
        // it with the remainder value, which will
        // be temp1 % temp2
        x.erase(x.begin());
        x.erase(x.begin());
        x.insert(x.begin(), temp1%temp2);
 
        //we then remove the first two values from
        //the list of modulii as we no longer require
        // them and simply replace them with the new
        // modulii that  we calculated
        m.erase(m.begin());
        m.erase(m.begin());
        m.insert(m.begin(), temp2);
 
        // once the list has only one element left,
        // we can break as it will only  contain
        // the value of our final remainder
        if(x.size()== 1){
            break;
        }
    }
         
    // returns the remainder of the final equation
    return x[0];
}
 
// driver segment
int main(){
    vector<int> m = {4, 7, 9, 11};
    vector<int> x = {3, 4, 1, 0};
    cout << crt(m, x) << endl;
    return 0;
}
 
// The code is contributed by Gautam goel (gautamgoe962)


Java




// Java program to implement the Chinese Remainder Theorem
import java.util.ArrayList;
import java.math.BigInteger;
 
public class ChineseRemainderTheorem {
 
    // Function to calculate the modular inverse of a and m
    public static BigInteger modinv(BigInteger a, BigInteger m) {
        BigInteger m0 = m;
        BigInteger y = BigInteger.ZERO;
        BigInteger x = BigInteger.ONE;
        if (m.equals(BigInteger.ONE))
            return BigInteger.ZERO;
        while (a.compareTo(BigInteger.ONE) == 1) {
            BigInteger q = a.divide(m);
            BigInteger t = m;
            m = a.mod(m);
            a = t;
            t = y;
            y = x.subtract(q.multiply(y));
            x = t;
        }
        if (x.compareTo(BigInteger.ZERO) == -1)
            x = x.add(m0);
        return x;
    }
 
    // Function to implement the Chinese Remainder Theorem
    public static BigInteger crt(ArrayList<BigInteger> m, ArrayList<BigInteger> x) {
        BigInteger M = BigInteger.ONE;
        for (int i = 0; i < m.size(); i++) {
            M = M.multiply(m.get(i));
        }
        BigInteger result = BigInteger.ZERO;
        for (int i = 0; i < m.size(); i++) {
            BigInteger Mi = M.divide(m.get(i));
            BigInteger MiInv = modinv(Mi, m.get(i));
            result = result.add(x.get(i).multiply(Mi).multiply(MiInv));
        }
        return result.mod(M);
    }
 
    public static void main(String[] args) {
        ArrayList<BigInteger> m = new ArrayList<>();
        ArrayList<BigInteger> x = new ArrayList<>();
        m.add(BigInteger.valueOf(4));
        m.add(BigInteger.valueOf(7));
        m.add(BigInteger.valueOf(9));
        m.add(BigInteger.valueOf(11));
        x.add(BigInteger.valueOf(3));
        x.add(BigInteger.valueOf(4));
        x.add(BigInteger.valueOf(1));
        x.add(BigInteger.valueOf(0));
        System.out.println(crt(m, x));
    }
}
 
// This code is contributed by Vikram_Shirsat


Python




# Python 2.x program to combine modular equations
# using Chinese Remainder Theorem
 
# function that implements Extended euclidean
# algorithm
def extended_euclidean(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = extended_euclidean(b % a, a)
        return (g, x - (b // a) * y, y)
 
# modular inverse driver function
def modinv(a, m):
    g, x, y = extended_euclidean(a, m)
    return x % m
 
# function implementing Chinese remainder theorem
# list m contains all the modulii
# list x contains the remainders of the equations
def crt(m, x):
 
    # We run this loop while the list of
    # remainders has length greater than 1
    while True:
         
        # temp1 will contain the new value
        # of A. which is calculated according
        # to the equation m1' * m1 * x0 + m0'
        # * m0 * x1
        temp1 = modinv(m[1],m[0]) * x[0] * m[1] + \
                modinv(m[0],m[1]) * x[1] * m[0]
 
        # temp2 contains the value of the modulus
        # in the new equation, which will be the
        # product of the modulii of the two
        # equations that we are combining
        temp2 = m[0] * m[1]
 
        # we then remove the first two elements
        # from the list of remainders, and replace
        # it with the remainder value, which will
        # be temp1 % temp2
        x.remove(x[0])
        x.remove(x[0])
        x = [temp1 % temp2] + x
 
        # we then remove the first two values from
        # the list of modulii as we no longer require
        # them and simply replace them with the new
        # modulii that  we calculated
        m.remove(m[0])
        m.remove(m[0])
        m = [temp2] + m
 
        # once the list has only one element left,
        # we can break as it will only  contain
        # the value of our final remainder
        if len(x) == 1:
            break
 
    # returns the remainder of the final equation
    return x[0]
 
# driver segment
m = [4, 7, 9, 11]
x = [3, 4, 1, 0]
print crt(m, x)


C#




using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
 
// C# program to combine modular equations
// using Chinese Remainder Theorem
class HelloWorld {
 
  // function that implements Extended euclidean
  // algorithm
  public static List<int> extended_euclidean(int a,int b){
    if(a == 0){
      List<int> temp = new List<int>();
      temp.Add(b);
      temp.Add(0);
      temp.Add(1);
      return temp;
    }
    else{
      List<int> temp = new List<int>();
      temp.Add(0);
      temp.Add(0);
      temp.Add(0);
      temp= extended_euclidean(b % a, a);
      int g = temp[0];
      int y = temp[1];
      int x = temp[2];
      temp[0] = g;
      temp[1] = x - ((b/a) * y);
      temp[2] = y;
      return temp;
    }
    List<int> temp1 = new List<int>();
 
    return temp1;
  }
 
  // modular inverse driver function
  public static double modinv(int a,int m){
    List<int> temp = new List<int>();
    temp.Add(0);
    temp.Add(0);
    temp.Add(0);
    temp = extended_euclidean(a, m);
    int g = temp[0];
    int x = temp[1];
    int y = temp[2];
 
    // Since we are taking the modulo of
    // negative numbers so, to have positive
    // output of the modulo we use this formula.
    double val = Math.Floor(((double)x/(double)m));
    double ans = x -  (val * m);
    return ans;
  }
 
  // function implementing Chinese remainder theorem
  // list m contains all the modulii
  // list x contains the remainders of the equations
  public static int crt(List<int> m,List<int> x)
  {
 
    // We run this loop while the list of
    // remainders has length greater than 1
    while(true)
    {
 
      // temp1 will contain the new value
      // of A. which is calculated according
      // to the equation m1' * m1 * x0 + m0'
      // * m0 * x1
      double var1 = (modinv(m[1],m[0]));
      double var2 = (modinv(m[0],m[1]));
      // cout << var1 << " " << var2 << endl;
      double temp1 = (modinv(m[1],m[0])) * x[0] * m[1] + (modinv(m[0],m[1]) )* x[1] * m[0];
 
      // temp2 contains the value of the modulus
      // in the new equation, which will be the
      // product of the modulii of the two
      // equations that we are combining
      int temp2 = m[0] * m[1];
      // cout << temp1<< " "<<temp2<< endl;
      // we then remove the first two elements
      // from the list of remainders, and replace
      // it with the remainder value, which will
      // be temp1 % temp2
      x.RemoveAt(0);
      x.RemoveAt(0);
      x.Insert(0, (int)temp1%(int)temp2);
 
      //we then remove the first two values from
      //the list of modulii as we no longer require
      // them and simply replace them with the new
      // modulii that  we calculated
      m.RemoveAt(0);
      m.RemoveAt(0);
      m.Insert(0, temp2);
 
      // once the list has only one element left,
      // we can break as it will only  contain
      // the value of our final remainder
      if(x.Count == 1){
        break;
      }
    }
 
    // returns the remainder of the final equation
    return x[0];
  }
 
  static void Main() {
    List<int> m = new List<int>(){
      4, 7, 9, 11
      };
    List<int> x = new List<int> (){
      3, 4, 1, 0
      };
    Console.WriteLine(crt(m, x));
  }
}
 
// The code is contributed by Nidhi goel.


Javascript




// JavaScript program to combine modular equations
// using Chinese Remainder Theorem
 
// function that implements Extended euclidean
// algorithm
function extended_euclidean(a, b){
    if(a == 0){
        let temp = [b, 0, 1];
        return temp;
    }
    else{
        let temp= extended_euclidean(b % a, a);
        let g = temp[0];
        let y = temp[1];
        let x = temp[2];
        temp[0] = g;
        temp[1] = x - (Math.floor(b/a) * y);
        temp[2] = y;
        return temp;
    }
    let temp;
    return temp;
}
 
// modular inverse driver function
function modinv(a, m){
    let temp = extended_euclidean(a, m);
    let g = temp[0];
    let x = temp[1];
    let y = temp[2];
     
    // Since we are taking the modulo of
    // negative numbers so, to have positive
    // output of the modulo we use this formula.
    let ans = x -  (Math.floor(x/m) * m);
    return ans;
}
     
 
// function implementing Chinese remainder theorem
// list m contains all the modulii
// list x contains the remainders of the equations
function crt(m, x)
{
   
    // We run this loop while the list of
    // remainders has length greater than 1
    while(1)
    {
       
        // temp1 will contain the new value
        // of A. which is calculated according
        // to the equation m1' * m1 * x0 + m0'
        // * m0 * x1
        let var1 = (modinv(m[1],m[0]));
        let var2 = (modinv(m[0],m[1]) );
        // cout << var1 << " " << var2 << endl;
        let temp1 = (modinv(m[1],m[0])) * x[0] * m[1] + (modinv(m[0],m[1]) )* x[1] * m[0];
 
        // temp2 contains the value of the modulus
        // in the new equation, which will be the
        // product of the modulii of the two
        // equations that we are combining
        let temp2 = m[0] * m[1];
        // cout << temp1<< " "<<temp2<< endl;
        // we then remove the first two elements
        // from the list of remainders, and replace
        // it with the remainder value, which will
        // be temp1 % temp2
        x.shift();
        x.shift();
        x.unshift(temp1 % temp2);
 
        //we then remove the first two values from
        //the list of modulii as we no longer require
        // them and simply replace them with the new
        // modulii that  we calculated
        m.shift();
        m.shift();
        m.unshift(temp2);
 
        // once the list has only one element left,
        // we can break as it will only  contain
        // the value of our final remainder
        if(x.length== 1){
            break;
        }
    }
         
    // returns the remainder of the final equation
    return x[0];
}
 
// driver segment
let m = [4, 7, 9, 11];
let x = [3, 4, 1, 0];
console.log(crt(m, x));
 
// The code is contributed by phasing17


Output:

1243

Time Complexity : O(l) ,where l is the size of remainder list.

Space Complexity : O(1) ,as we are not using any extra space.

This theorem and algorithm has excellent applications. One very useful application is in calculating nCr % m where m is not a prime number, and Lucas Theorem cannot be directly applied. In such a case, we can calculate the prime factors of m, and use the prime factors one by one as a modulus in our nCr % m equation which we can calculate using Lucas Theorem, and then combine the resulting equations together using the above shown Chinese Remainder Theorem.



Last Updated : 23 Feb, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads