This repository has been archived on 2022-06-22. You can view files and clone it, but cannot push or open issues or pull requests.
usaco-guide/content/4_Gold/Intro_NT.mdx
2020-07-18 21:36:48 -04:00

230 lines
9 KiB
Text

---
id: intro-nt
title: "Introductory Number Theory"
author: Darren Yao, Michael Cao
prerequisites:
- intro-dp
- binary representation
description: Introduces divisibility and modular arithmetic.
frequency: 1
---
import { Problem } from "../models";
export const metadata = {
problems: {
sample: [
new Problem("CF", "VK Cup Wildcard R1 C - Prime Factorization", "problemset/problem/162/C", "Intro|Very Easy", false, []),
],
kat: [
new Problem("Kattis","Modular Arithmetic", "modulararithmetic"),
],
general: [
new Problem("AC", "Div Game", "https://atcoder.jp/contests/abc169/tasks/abc169_d", "Easy", false, ["Prime Factorization"], "Prime factorize the given number. Consider each prime in the factorization separately. For each prime, decrement the exponent by 1 the first time, 2 the second time, and so on, until we can no longer continue without repeating a previously used exponent."),
new Problem("CF", "Orac and LCM", "contest/1349/problem/A", "Normal", false, ["Prime Factorization"], "Prime factorize each number. For each prime, the second-to-lowest exponent of the prime that occurs in any of the numbers in the input is the exponent of this prime that will appear in the final answer."),
new Problem("Gold", "Cow Poetry", "897", "Normal", false, ["Knapsack", "Exponentiation"], "First consider the case where there are only two lines with the same class."),
new Problem("Gold", "Exercise", "1043", "Normal", false, ["Knapsack", "Prime Factorization"], "Prime factorize $K$."),
],
}
};
<Resources>
<Resource source="IUSACO" title="13 - Elementary Number Theory">module is based off this</Resource>
<Resource source="AoPS" title="Alcumus" url="https://artofproblemsolving.com/alcumus/problem" starred>practice problems, set focus to number theory!</Resource>
<Resource source="AoPS" title ="Intro to NT" url="https://artofproblemsolving.com/store/item/intro-number-theory?gtmlist=Bookstore_AoPS_Side">good book :D</Resource>
<Resource source="CPH" title="21.1, 21.2 - Number Theory">primes and factors, modular arithmetic</Resource>
<Resource source="PAPS" title="16 - Number Theory">same as below</Resource>
<Resource source="CF" title="CodeNCode - Number Theory Course" url="blog/entry/77137">lots of advanced stuff you don't need to know at this level</Resource>
</Resources>
## Prime Factorization
<Problems problems={metadata.problems.sample} />
A number $a$ is called a **divisor** or a **factor** of a number $b$ if $b$ is divisible by $a$, which means that there exists some integer $k$ such that $b = ka$. Conventionally, $1$ and $n$ are considered divisors of $n$. A number $n > 1$ is **prime** if its only divisors are $1$ and $n$. Numbers greater than \(1\) that are not prime are **composite**.
Every number has a unique **prime factorization**: a way of decomposing it into a product of primes, as follows:
$$
n = {p_1}^{a_1} {p_2}^{a_2} \cdots {p_k}^{a_k}
$$
where the $p_i$ are distinct primes and the $a_i$ are positive integers.
Now, we will discuss how to find the prime factorization of an integer.
![Pseudocode](factoralgorithm1.png)
<IncompleteSection>
replace with code in all langs?
</IncompleteSection>
This algorithm runs in $O(\sqrt{n})$ time, because the for loop checks divisibility for at most $\sqrt{n}$ values. Even though there is a while loop inside the for loop, dividing $n$ by $i$ quickly reduces the value of $n$, which means that the outer for loop runs less iterations, which actually speeds up the code.
Let's look at an example of how this algorithm works, for $n = 252$.
![Example](factoralgorithm2.png)
At this point, the for loop terminates, because $i$ is already 3 which is greater than $\lfloor \sqrt{7} \rfloor$. In the last step, we add $7$ to the list of factors $v$, because it otherwise won't be added, for a final prime factorization of $\{2, 2, 3, 3, 7\}$.
## Divisibility
### GCD
The **greatest common divisor (GCD)** of two integers $a$ and $b$ is the largest integer that is a factor of both $a$ and $b$. In order to find the GCD of two numbers, we use the **Euclidean Algorithm**, which is as follows:
$$
\gcd(a, b) = \begin{cases}
a & b = 0 \\
\gcd(b, a \bmod b) & b \neq 0 \\
\end{cases}
$$
This algorithm is very easy to implement using a recursive function, as follows:
<LanguageSection>
<JavaSection>
```java
public int gcd(int a, int b){
if (b == 0) return a;
return gcd(b, a % b);
}
```
</JavaSection>
<CPPSection>
```cpp
int GCD(int a, int b){
if (b == 0) return a;
return GCD(b, a % b);
}
```
For C++14 and below, use the built-in `__gcd(a,b)`. C++17 has built in `gcd(a,b)`.
</CPPSection>
</LanguageSection>
This function runs in $O(\log \max(a,b))$ time.
<IncompleteSection>
proof?
</IncompleteSection>
### LCM
The **least common multiple (LCM)** of two integers $a$ and $b$ is the smallest integer divisible by both $a$ and $b$.
The LCM can easily be calculated from the following property with the GCD:
$$
\operatorname{lcm}(a, b) = \frac{a \cdot b}{\gcd(a, b)}=\frac{a}{\gcd(a,b)}\cdot b.
$$
<Warning>
Dividing by $\gcd(a,b)$ first might prevent integer overflow.
</Warning>
If we want to take the GCD or LCM of more than two elements, we can do so two at a time, in any order. For example,
$$
\gcd(a_1, a_2, a_3, a_4) = \gcd(a_1, \gcd(a_2, \gcd(a_3, a_4))).
$$
## Modular Arithmetic
<Resources>
<Resource source="David Altizio" title="Modular Arithmetic" url="https://davidaltizio.web.illinois.edu/ModularArithmetic.pdf" starred> </Resource>
</Resources>
In **modular arithmetic**, instead of working with integers themselves, we work with their remainders when divided by $m$. We call this taking modulo $m$. For example, if we take $m = 23$, then instead of working with $x = 247$, we use $x \bmod 23 = 17$. Usually, $m$ will be a large prime, given in the problem; the two most common values are $10^9 + 7$, and $998\,244\,353=119\cdot 2^{23}+1$. Modular arithmetic is used to avoid dealing with numbers that overflow built-in data types, because we can take remainders, according to the following formulas:
$$
(a+b) \bmod m = (a \bmod m + b \bmod m) \bmod m
$$
$$
(a-b) \bmod m = (a \bmod m - b \bmod m) \bmod m
$$
$$
(a \cdot b) \pmod{m} = ((a \bmod m) \cdot (b \bmod m)) \bmod m
$$
$$
a^b \bmod {m} = (a \bmod m)^b \bmod m
$$
### Modular Exponentiation
<Resources>
<Resource source="cp-algo" title="Binary Exponentiation" url="algebra/binary-exp.html"> </Resource>
</Resources>
**Modular Exponentiation** can be used to efficently compute $x ^ n \mod m$. To do this, let's break down $x ^ n$ into binary components. For example, $5 ^ {10}$ = $5 ^ {1010_2}$ = $5 ^ 8 \cdot 5 ^ 4$. Then, if we know $x ^ y$ for all $y$ which are powers of two ($x ^ 1$, $x ^ 2$, $x ^ 4$, $\dots$ , $x ^ {2^{\lfloor{\log_2n} \rfloor}}$, we can compute $x ^ n$ in $\mathcal{O}(\log n)$. Finally, since $x ^ y$ for some $y \neq 1$ equals $2 \cdot x ^ {y - 1}$, and $x$ otherwise, we can compute these sums efficently. To deal with $m$, observe that modulo doesn't affect multiplications, so we can directly implement the above "binary exponentiation" algorithm while adding a line to take results $\pmod m$.
Here is code to compute $x ^ n \pmod m$:
<LanguageSection>
<CPPSection>
```cpp
long long binpow(long long x, long long n, long long m) {
x %= m; //note: m*m must be less than 2^63-1 to avoid long long overflow
long long res = 1;
while (n > 0) {
if (n % 2 == 1) //if n is odd
res = res * x % m;
x = x * x % m;
n /= 2; //divide by two
}
return res;
}
```
</CPPSection>
</LanguageSection>
### Modular Inverse
We'll only consider **prime** moduli here. Division can be performed using modular inverses.
<Resources>
<Resource source="cp-algo" title="Modular Multiplicative Inverse" url="algebra/module-inverse.html">Various ways to take modular inverse</Resource>
</Resources>
#### With Exponentiation
To find the modular inverse of some number, simply raise it to the power of $\mathrm{MOD} - 2$, where $\mathrm{MOD}$ is the modulus being used, using the function described above. (The reasons for raising the number to $\mathrm{MOD} - 2$ can be explained with **Fermat's Little Theorem**, but we will not explain the theorem here).
The modular inverse is the equivalent of the reciprocal in real-number arithmetic; to divide $a$ by $b$, multiply $a$ by the modular inverse of $b$.
Because it takes $\mathcal{O}(\log \mathrm{MOD})$ time to compute a modular inverse, frequent use of division inside a loop can significantly increase the running time of a program. If the modular inverse of the same number(s) is/are being used many times, it is a good idea to precalculate it.
Also, one must always ensure that they do not attempt to divide by 0. Be aware that after applying modulo, a nonzero number can become zero, so be very careful when dividing by non-constant values.
<IncompleteSection />
#### With Extended Euclidean Algorithm
<Optional>
See the module in the [Advanced](../adv/extend-euclid) section.
</Optional>
## Problems
<Problems problems={metadata.problems.general} />