Number Theory Fundamentals for Competitive Programming

Introduction to essential number theory concepts such as prime numbers, GCD, LCM, modular arithmetic, and their applications in competitive coding problems with Java examples.


Greatest Common Divisor (GCD): Euclidean Algorithm and Applications

Understanding the Greatest Common Divisor (GCD)

The Greatest Common Divisor (GCD) of two or more integers is the largest positive integer that divides each of the integers without a remainder. For example, the GCD of 12 and 18 is 6. GCD plays a fundamental role in number theory and has diverse applications in competitive programming.

The Euclidean Algorithm

The Euclidean algorithm is an efficient method for computing the GCD of two integers. It is based on the principle that the GCD of two numbers does not change if the larger number is replaced by its difference with the smaller number. This process is repeated until one of the numbers becomes zero, at which point the other number is the GCD. A more efficient version uses the modulo operator instead of subtraction.

Recursive Implementation in Java

 public class GCD {
    public static int gcd(int a, int b) {
        if (b == 0) {
            return a;
        }
        return gcd(b, a % b);
    }

    public static void main(String[] args) {
        int a = 48;
        int b = 18;
        System.out.println("GCD of " + a + " and " + b + " is: " + gcd(a, b)); // Output: 6
    }
} 

Iterative Implementation in Java

 public class GCDIterative {
    public static int gcd(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }

    public static void main(String[] args) {
        int a = 48;
        int b = 18;
        System.out.println("GCD of " + a + " and " + b + " is: " + gcd(a, b)); // Output: 6
    }
} 

Extended Euclidean Algorithm

The Extended Euclidean Algorithm not only finds the GCD of two integers, a and b, but also finds integers x and y such that:

ax + by = gcd(a, b)
The integers x and y are known as Bézout's coefficients. This algorithm is crucial for solving linear Diophantine equations and finding modular inverses.

Implementation in Java

 public class ExtendedGCD {
    public static int extendedGCD(int a, int b, int[] result) {
        if (a == 0) {
            result[0] = 0; // x
            result[1] = 1; // y
            return b;
        }

        int[] temp = new int[2];
        int gcd = extendedGCD(b % a, a, temp);

        result[0] = temp[1] - (b / a) * temp[0];
        result[1] = temp[0];

        return gcd;
    }

    public static void main(String[] args) {
        int a = 48;
        int b = 18;
        int[] result = new int[2]; // result[0] = x, result[1] = y
        int gcd = extendedGCD(a, b, result);

        System.out.println("GCD of " + a + " and " + b + " is: " + gcd); // Output: 6
        System.out.println("x = " + result[0]); // Output: x = -1
        System.out.println("y = " + result[1]); // Output: y = 3
        System.out.println("(" + a + "*" + result[0] + ") + (" + b + "*" + result[1] + ") = " + gcd); // Example: (48*-1) + (18*3) = 6
    }
} 

Applications in Solving Linear Diophantine Equations

A linear Diophantine equation is an equation of the form:

ax + by = c
where a, b, and c are integers, and we are looking for integer solutions for x and y. A solution exists if and only if gcd(a, b) divides c. If a solution exists, we can find a particular solution using the Extended Euclidean Algorithm and then generate all possible solutions.

Solving Diophantine Equations in Java

 public class DiophantineSolver {

    public static boolean solveDiophantine(int a, int b, int c, int[] solution) {
        int[] egcdResult = new int[2];
        int gcd = ExtendedGCD.extendedGCD(a, b, egcdResult); // Assuming ExtendedGCD class exists

        if (c % gcd != 0) {
            return false; // No solution exists
        }

        int x0 = egcdResult[0] * (c / gcd);
        int y0 = egcdResult[1] * (c / gcd);

        solution[0] = x0;
        solution[1] = y0;
        return true;
    }

    public static void main(String[] args) {
        int a = 12;
        int b = 18;
        int c = 30;

        int[] solution = new int[2];
        if (solveDiophantine(a, b, c, solution)) {
            System.out.println("Diophantine Equation " + a + "x + " + b + "y = " + c + " has a solution:");
            System.out.println("x = " + solution[0]);
            System.out.println("y = " + solution[1]);
        } else {
            System.out.println("Diophantine Equation " + a + "x + " + b + "y = " + c + " has no solution.");
        }
    }
}


class ExtendedGCD { // Necessary since the other example uses this class
    public static int extendedGCD(int a, int b, int[] result) {
        if (a == 0) {
            result[0] = 0; // x
            result[1] = 1; // y
            return b;
        }

        int[] temp = new int[2];
        int gcd = extendedGCD(b % a, a, temp);

        result[0] = temp[1] - (b / a) * temp[0];
        result[1] = temp[0];

        return gcd;
    }
} 

GCD in Competitive Programming

GCD is frequently used in competitive programming problems, often as a subroutine or a key component of a larger algorithm. Examples include:

  • Simplifying fractions
  • Solving modular arithmetic problems
  • Finding the lowest common multiple (LCM)
  • Problems related to number theory and cryptography.
Efficient GCD calculation is crucial, and the Euclidean Algorithm provides a fast and reliable method.