Fundamentals of Number Theory

Suggest Changes
9/13/2024 · Authored by 

Notice

This article has been translated by AI for your convenience. Please note that there may be inaccuracies or differences from the original text.

GCD, Modular Arithmetic, and Congruences

Euclidean Algorithm

When learning algorithms, we often encounter the need to calculate the GCD (Greatest Common Divisor) of large numbers. Using trial division is too slow in such cases, so we use the method of successive division, also known as the Euclidean Algorithm, to speed up the process.

Modular arithmetic has the following property: (ab) mod p=((a mod p)(b mod p)) mod p(a*b)\ \text{mod} \ p=((a\ \text{mod} \ p)*(b\ \text{mod} \ p))\ \text{mod}\ p

Congruences and Their Properties

If aa is an integer and nn is a positive integer, we define the remainder of aa divided by nn as amodna \mod n, where nn is called the modulus. If (a mod n)=(b mod n)(a\ \text{mod}\ n)=(b\ \text{mod}\ n), we say aa and bb are congruent modulo nn, denoted as ab(mod n)a \equiv b (\text{mod}\ n).

Congruences have the following properties:

  1. If n(ab)n|(a-b), then ab(mod n)a \equiv b (\text{mod}\ n).
  2. If ab(mod n)a \equiv b (\text{mod}\ n), then ba(mod n)b \equiv a (\text{mod}\ n).
  3. If ab(mod n)a \equiv b (\text{mod}\ n) and bc(mod n)b \equiv c (\text{mod}\ n), then ac(mod n)a \equiv c (\text{mod}\ n).

Prime Numbers

Prime numbers are at the core of number theory. A prime number has only two factors: 1 and itself. Any composite number can be expressed as:

a=pPpap,ap0a = \prod \limits_{p \in P} p^{a_p}, a_p \geq 0

Here, PP represents all prime numbers, and most of the apa_p are 0.

Fermat's Theorem and Euler's Theorem

Fermat's Theorem

A useful representation of Fermat's Theorem is:

If pp is a prime number and aa is any positive integer, then:

apa (mod p)a^p \equiv a \ (\text{mod}\ p)

We won't prove this here, but as an example: For a=3a=3 and p=5p=5, 35=243mod5=33^5 = 243 \mod 5 = 3, and 3mod5=33 \mod 5 = 3.

Euler's Totient Function

The Euler's totient function ϕ(n)\phi(n) is the count of positive integers less than nn that are coprime to nn. By convention, ϕ(1)=1\phi(1)=1. The function has the following properties:

  • For a prime nn, ϕ(n)=n1\phi(n) = n-1.

  • For two primes pp and qq, if n=pqn = pq, then:

    ϕ(n)=ϕ(pq)=ϕ(p)ϕ(q)=(p1)(q1)\phi(n) = \phi(pq) = \phi(p) \phi(q) = (p-1)(q-1)

Euler's Theorem

Euler's Theorem states that for any aa and nn that are coprime:

aϕ(n)1(mod n)a^{\phi(n)} \equiv 1 (\text{mod}\ n)

For example:

a=3, n=10, ϕ(10)=4, aϕ(n)=34=811 (mod 10)=1(mod n)a = 3, \ n = 10, \ \phi(10) = 4, \ a^{\phi(n)} = 3^4 = 81 \equiv 1 \ (\text{mod}\ 10) = 1 (\text{mod}\ n)

Similar to Fermat's Theorem, Euler's Theorem also has an alternative form:

aϕ(n)+1a(mod n)a^{\phi(n)+1} \equiv a (\text{mod}\ n)

In this case, aa and pp do not need to be coprime.

Primality Testing

How can we efficiently construct a large prime number? At the time of writing, there isn't a definitive algorithm to reliably construct large primes. However, there is an algorithm that can almost certainly construct a large prime: the Miller-Rabin Algorithm.

Miller-Rabin Algorithm

The Miller-Rabin Algorithm uses properties of primes for quick checks. It can efficiently determine that a large number is not prime, but it cannot definitively confirm that it is prime. The concept is somewhat similar to a Bloom filter.

For any odd number greater than 1, it can be expressed as n1=2kq, k>0, qn-1 = 2^kq, \ k > 0, \ q is odd.

Using the properties outlined earlier, we can quickly determine cases where a large number is not prime. Here's an example in C++:

bool MillerRabinTest(const std::int64_t n) {
  // find k, q that satisfies n-1 == (2^k) * q
  std::int64_t temp = n;
  temp--;
  std::int64_t k = 0;
  while (temp % 2 == 0) {
    temp /= 2;
    k++;
  }
  std::int64_t q = temp;
  // randomly choose an integer a, 1 < a < n-1
  std::mt19937_64 rng(
      std::chrono::steady_clock::now().time_since_epoch().count());
  std::uniform_int_distribution<std::int64_t> range(1, n - 1);
  std::int64_t a = range(rng);
  if (QuickPower(a, q) % n == 1) {
    return true;
  }
  for (auto j = 0ll; j < k; j++) {
    if (QuickPower(a, (1ll << j) * q) % n == n - 1) {
      return true;
    }
  }
  return false;
}

Repeated Use of the Miller-Rabin Algorithm

How can we use the Miller-Rabin Algorithm to increase the confidence of determining whether a number is prime? By choosing different values of aa and running multiple tests. If the number of test cases t=10t=10, the probability of a composite number passing all tests is less than 10610^{-6}. Therefore, selecting a sufficiently large tt makes it highly likely that nn is a prime.

In cryptography, large primes are often used for encryption and other operations. For example, RSA requires the generation of large primes for its factorization-based security.