오일러 피 함수¶
피 함수(phi-function) $\phi (n)$라고도 불리는 오일러 피 함수는 1 이상 $n$ 이하의 정수 중 $n$과 서로소인 정수의 개수를 셉니다. 두 수의 최대공약수가 $1$이면 두 수는 서로소입니다($1$은 모든 수와 서로소로 간주합니다).
처음 몇 개의 양의 정수에 대한 $\phi(n)$의 값은 다음과 같습니다:
성질¶
오일러 피 함수의 다음 성질들만 알면 어떤 수에 대해서든 함숫값을 계산할 수 있습니다:
- $p$가 소수이면 모든 $1 \le q < p$에 대해 $\gcd(p, q) = 1$입니다. 따라서 다음이 성립합니다:
- $p$가 소수이고 $k \ge 1$이면, $1$과 $p^k$ 사이에 $p$로 나누어떨어지는 수는 정확히 $p^k / p$개입니다. 이로부터 다음을 얻습니다:
-
$a$와 $b$가 서로소이면 다음이 성립합니다:
$$\phi(a b) = \phi(a) \cdot \phi(b).$$이 관계는 바로 알아보기 쉽지 않습니다. 이는 중국인의 나머지 정리로부터 유도됩니다. 중국인의 나머지 정리는 각 $0 \le x < a$와 각 $0 \le y < b$에 대해 $z \equiv x \pmod{a}$이고 $z \equiv y \pmod{b}$인 $0 \le z < a b$가 유일하게 존재함을 보장합니다. $z$가 $a b$와 서로소인 것은 $x$가 $a$와 서로소이고 $y$가 $b$와 서로소인 것과 동치임을 어렵지 않게 보일 수 있습니다. 따라서 $a b$와 서로소인 정수의 개수는 $a$와 $b$ 각각에 대한 개수의 곱과 같습니다.
-
일반적으로, 서로소가 아닌 $a$와 $b$에 대해 다음 방정식은
$$\phi(ab) = \phi(a) \cdot \phi(b) \cdot \dfrac{d}{\phi(d)}$$$d = \gcd(a, b)$일 때 성립합니다.
따라서 처음 세 가지 성질을 이용하면 $n$의 소인수분해($n$을 소인수들의 곱으로 분해하는 것)를 통해 $\phi(n)$을 계산할 수 있습니다. $n = {p_1}^{a_1} \cdot {p_2}^{a_2} \cdots {p_k}^{a_k}$이고 $p_i$가 $n$의 소인수라면,
구현¶
다음은 소인수분해를 이용하여 $O(\sqrt{n})$에 동작하는 구현입니다:
int phi(int n) {
int result = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
while (n % i == 0)
n /= i;
result -= result / i;
}
}
if (n > 1)
result -= result / n;
return result;
}
$O(n \log\log{n})$에 $1$부터 $n$까지의 오일러 피 함수 구하기¶
$1$부터 $n$까지의 모든 수에 대한 오일러 피 함수 값이 필요하다면, $n$개의 수를 모두 인수분해하는 것은 효율적이지 않습니다. 에라토스테네스의 체와 같은 아이디어를 사용할 수 있습니다. 이 방법도 여전히 위에서 보인 성질에 기반하지만, 각 수마다 각 소인수에 대해 임시 결과를 갱신하는 대신, 모든 소수를 찾은 뒤 각 소수에 대해 그 소수로 나누어떨어지는 모든 수의 임시 결과를 갱신합니다.
이 방법은 에라토스테네스의 체와 사실상 동일하므로 복잡도도 $O(n \log \log n)$으로 같습니다.
void phi_1_to_n(int n) {
vector<int> phi(n + 1);
for (int i = 0; i <= n; i++)
phi[i] = i;
for (int i = 2; i <= n; i++) {
if (phi[i] == i) {
for (int j = i; j <= n; j += i)
phi[j] -= phi[j] / i;
}
}
}
구간 체를 이용하여 $L$부터 $R$까지의 오일러 피 함수 값 구하기¶
$L$부터 $R$까지의 모든 수에 대한 오일러 피 함수 값이 필요하다면, 구간 체 방식을 사용할 수 있습니다.
이 알고리즘은 먼저 선형 체를 사용하여 $\sqrt{R}$ 이하의 모든 소수를 $O(\sqrt{R})$의 시간과 공간으로 미리 계산합니다. 그런 다음 구간 $[L, R]$의 각 수에 대해 이 소수들을 순회하며 소인수분해 기반의 $\phi$ 공식을 적용합니다. 각 수에서 아직 소인수분해되지 않은 부분을 추적하기 위해 나머지 배열을 유지합니다. 작은 소수들을 모두 처리한 뒤에도 나머지가 여전히 1보다 크다면, 이는 $\sqrt{R}$보다 큰 소인수가 있다는 뜻이며, 이 소인수는 마지막 단계에서 처리합니다. 구간 계산의 전체 복잡도는 $O((R - L + 1) \log \log R) + \sqrt{R}$입니다.
const long long MAX_RANGE = 1e6 + 6;
vector<long long> primes;
long long phi[MAX_RANGE], rem[MAX_RANGE];
vector<int> linear_sieve(int n) {
vector<bool> composite(n + 1, 0);
vector<int> prime;
// 0 and 1 are not composite (nor prime)
composite[0] = composite[1] = 1;
for(int i = 2; i <= n; i++) {
if(!composite[i]) prime.push_back(i);
for(int j = 0; j < prime.size() && i * prime[j] <= n; j++) {
composite[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
return prime;
}
// To get the value of phi(x) for L <= x <= R, use phi[x - L].
void segmented_phi(long long L, long long R) {
for(long long i = L; i <= R; i++) {
rem[i - L] = i;
phi[i - L] = i;
}
for(long long i : primes) {
for(long long j = max(i * i, (L + i - 1) / i * i); j <= R; j += i) {
phi[j - L] -= phi[j - L] / i;
while(rem[j - L] % i == 0) rem[j - L] /= i;
}
}
for(long long i = 0; i < R - L + 1; i++) {
if(rem[i] > 1) phi[i] -= phi[i] / rem[i];
}
}
약수 합 성질¶
이 흥미로운 성질은 Gauss가 밝혔습니다:
여기서 합은 $n$의 모든 양의 약수 $d$에 대해 취합니다.
예를 들어 10의 약수는 1, 2, 5, 10입니다. 따라서 $\phi{(1)} + \phi{(2)} + \phi{(5)} + \phi{(10)} = 1 + 1 + 4 + 4 = 10$입니다.
약수 합 성질을 이용하여 1부터 $n$까지의 오일러 피 함수 값 구하기¶
약수 합 성질을 이용해서도 1부터 $n$까지의 모든 수에 대한 오일러 피 함수 값을 계산할 수 있습니다. 이 구현은 앞서 살펴본 에라토스테네스의 체 기반 구현보다 조금 더 간단하지만, 복잡도는 약간 더 나쁩니다: $O(n \log n)$
void phi_1_to_n(int n) {
vector<int> phi(n + 1);
phi[0] = 0;
phi[1] = 1;
for (int i = 2; i <= n; i++)
phi[i] = i - 1;
for (int i = 2; i <= n; i++)
for (int j = 2 * i; j <= n; j += i)
phi[j] -= phi[i];
}
오일러 정리에서의 활용¶
오일러 피 함수의 가장 유명하고 중요한 성질은 오일러 정리로 표현됩니다:
$m$이 소수인 특수한 경우, 오일러 정리는 페르마의 소정리가 됩니다:
오일러 정리와 오일러 피 함수는 실제 응용에서 꽤 자주 등장하며, 예를 들어 둘 다 모듈러 곱셈 역원을 계산하는 데 사용됩니다.
이로부터 바로 다음과 같은 동치 관계도 얻습니다:
이를 이용하면 매우 큰 $n$에 대해서도 $x^n \bmod m$을 계산할 수 있습니다. $n$을 모듈로 아래에서 계산할 수 있게 해 주므로, 특히 $n$이 다른 계산의 결과인 경우에 유용합니다.
군론¶
$\phi(n)$은 법 n에 대한 곱셈군의 위수입니다. 이 곱셈군 $(\mathbb Z / n\mathbb Z)^\times$는 가역원(곱셈 역원을 갖는 원소)들로 이루어진 군입니다. 곱셈 역원을 갖는 원소는 정확히 $n$과 서로소인 원소들입니다.
법 $n$에 대한 원소 $a$의 곱셈 위수는 $\operatorname{ord}_n(a)$로 표기하며, $a^k \equiv 1 \pmod n$을 만족하는 가장 작은 $k>0$입니다. $\operatorname{ord}_n(a)$는 $a$가 생성하는 부분군의 크기이므로, 라그랑주 정리에 의해 임의의 $a$의 곱셈 위수는 반드시 $\phi(n)$을 나누어야 합니다. $a$의 곱셈 위수가 가능한 최댓값인 $\phi(n)$이라면, $a$는 원시근이며 정의에 따라 이 군은 순환군입니다.
일반화¶
마지막 동치 관계에는 덜 알려진 형태가 있는데, 이를 이용하면 $x$와 $m$이 서로소가 아닌 경우에도 $x^n \bmod m$을 효율적으로 계산할 수 있습니다. 임의의 $x, m$과 $n \geq \log_2 m$에 대해 다음이 성립합니다:
증명:
$p_1, \dots, p_t$를 $x$와 $m$의 공통 소수 약수라 하고, $k_i$를 $m$에서의 그 지수라 합시다. 이들을 이용해 $a = p_1^{k_1} \dots p_t^{k_t}$로 정의하면, $\frac{m}{a}$는 $x$와 서로소가 됩니다. 그리고 $k$를 $a$가 $x^k$를 나누는 가장 작은 수라 합시다. $n \ge k$라고 가정하면, 다음과 같이 쓸 수 있습니다:
세 번째 줄과 네 번째 줄이 동치인 것은 $ab \bmod ac = a(b \bmod c)$라는 사실에서 따라 나옵니다. 실제로 $r < c$인 $b = cd + r$이면, $ab = acd + ar$이고 $ar < ac$입니다.
$x$와 $\frac{m}{a}$는 서로소이므로, 오일러 정리를 적용하여 다음과 같은 효율적인($k$가 매우 작기 때문입니다. 실제로 $k \le \log_2 m$입니다) 공식을 얻을 수 있습니다:
이 공식은 적용하기 어렵지만, $x^n \bmod m$의 동작을 분석하는 데 사용할 수 있습니다. 거듭제곱의 수열 $(x^1 \bmod m, x^2 \bmod m, x^3 \bmod m, \dots)$은 처음 $k$개(또는 그 이하)의 원소 이후에 길이가 $\phi\left(\frac{m}{a}\right)$인 사이클에 들어감을 알 수 있습니다. $\phi\left(\frac{m}{a}\right)$는 $\phi(m)$을 나누므로($a$와 $\frac{m}{a}$가 서로소이므로 $\phi(a) \cdot \phi\left(\frac{m}{a}\right) = \phi(m)$이 성립합니다), 주기의 길이가 $\phi(m)$이라고도 말할 수 있습니다. 그리고 $\phi(m) \ge \log_2 m \ge k$이므로, 원하던 훨씬 더 간단한 공식을 결론으로 얻을 수 있습니다:
연습 문제¶
- SPOJ #4141 "Euler Totient Function" [Difficulty: CakeWalk]
- UVA #10179 "Irreducible Basic Fractions" [Difficulty: Easy]
- UVA #10299 "Relatives" [Difficulty: Easy]
- UVA #11327 "Enumerating Rational Numbers" [Difficulty: Medium]
- TIMUS #1673 "Admission to Exam" [Difficulty: High]
- UVA 10990 - Another New Function
- Codechef - Golu and Sweetness
- SPOJ - LCM Sum
- GYM - Simple Calculations (F)
- UVA 13132 - Laser Mirrors
- SPOJ - GCDEX
- UVA 12995 - Farey Sequence
- SPOJ - Totient in Permutation (easy)
- LOJ - Mathematically Hard
- SPOJ - Totient Extreme
- SPOJ - Playing with GCD
- SPOJ - G Force
- SPOJ - Smallest Inverse Euler Totient Function
- Codeforces - Power Tower
- Kattis - Exponial
- LeetCode - 372. Super Pow
- Codeforces - The Holmes Children
- Codeforces - Small GCD