오일러 피 함수

December 26, 2021

본 포스트는 e-maxx.ru/algo 의 영문 번역본인 cp-algorithms (e-maxx-eng) 를 한국어로 번역한 것입니다. e-maxx 포스트의 저자는 иванов максим 이며, cp-algorithms 포스트의 기여자는 여기서 확인하실 수 있습니다. 본 포스트는 CC-BY-SA-4.0 License를 따릅니다.

오일러 피 함수

오일러 피 함수 (Euler’s phi (totient) function)) ϕ(n)\phi(n)는 1부터 nn까지의 정수 중 nn과 서로소인 것의 개수를 세는 함수이다. 두 수의 최대공약수가 1일 때, 두 수는 서로소이다(1은 모든 수와 서로소인 것으로 간주된다).

처음 몇 개의 자연수에 대해 ϕ(n)\phi(n)의 값은 다음과 같다:

n123456789101112131415161718192021ϕ(n)11224264641041268816618812\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|} \hline n & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 & 21 \\ \hline \phi(n) & 1 & 1 & 2 & 2 & 4 & 2 & 6 & 4 & 6 & 4 & 10 & 4 & 12 & 6 & 8 & 8 & 16 & 6 & 18 & 8 & 12 \\ \hline \end{array}

성질

오일러 피 함수의 다음 성질들을 이용하여 모든 수에 대해 오일러 피 함수를 계산할 수 있다.

  • pp가 소수일 때, 모든 1q<p1 \le q < p에 대해 gcd(p,q)=1\gcd(p, q) = 1이다. 따라서 다음 등식을 얻는다:

    ϕ(p)=p1.\phi (p) = p - 1.
  • pp가 소수이고 k1k \ge 1일 때, 11부터 pkp^k까지의 수 중 정확히 pk/pp^k / p개가 pp로 나누어 떨어진다. 따라서 다음 등식을 얻는다:

    ϕ(pk)=pkpk1.\phi(p^k) = p^k - p^{k-1}.
  • aa, bb가 서로소일 때, 다음 등식을 얻는다:

    ϕ(ab)=ϕ(a)ϕ(b).\phi(a b) = \phi(a) \cdot \phi(b).

    이 관계는 자명해 보이지 않는다. 이 결과는 중국인 나머지 정리로부터 알 수 있는데, 중국인 나머지 정리에 의해 각 0x<a0 \le x < a, 0y<b0 \le y < b에 대해 zx(moda)z \equiv x \pmod{a}, zy(modb)z \equiv y \pmod{b}를 만족하는 유일한 0z<ab0 \le z < a b가 존재한다. zzaba b과 서로소임은 xxaa와 서로소이고 yybb와 서로소임과 동치이다. 따라서 aba b와 서로소인 정수의 개수는 φ(a)\varphi(a), φ(b)\varphi(b)의 곱과 같다.

  • 서로소가 아닌 두 수 aa, bb에 대해서, 등식 ϕ(ab)=ϕ(a)ϕ(b)dϕ(d)\phi(ab) = \phi(a) \cdot \phi(b) \cdot \dfrac{d}{\phi(d)} 이 성립한다. 이때, d=gcd(a,b)d = \gcd(a, b)이다.

처음 세 성질과 nn의 소인수분해를 이용하여 ϕ(n)\phi(n)를 계산할 수 있다. n=p1a1p2a2pkakn = {p_1}^{a_1} \cdot {p_2}^{a_2} \cdots {p_k}^{a_k}, pip_i는 각각 소수일 때, 다음 등식이 성립한다.

ϕ(n)=ϕ(p1a1)ϕ(p2a2)ϕ(pkak)=(p1a1p1a11)(p2a2p2a21)(pkakpkak1)=p1a1(11p1)p2a2(11p2)pkak(11pk)=n(11p1)(11p2)(11pk)\begin{align*} \phi (n) &= \phi ({p_1}^{a_1}) \cdot \phi ({p_2}^{a_2}) \cdots \phi ({p_k}^{a_k}) \\ &= \left({p_1}^{a_1} - {p_1}^{a_1 - 1}\right) \cdot \left({p_2}^{a_2} - {p_2}^{a_2 - 1}\right) \cdots \left({p_k}^{a_k} - {p_k}^{a_k - 1}\right) \\ &= p_1^{a_1} \cdot \left(1 - \frac{1}{p_1}\right) \cdot p_2^{a_2} \cdot \left(1 - \frac{1}{p_2}\right) \cdots p_k^{a_k} \cdot \left(1 - \frac{1}{p_k}\right) \\ &= n \cdot \left(1 - \frac{1}{p_1}\right) \cdot \left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_k}\right) \end{align*}

구현

O(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;
}

11부터 nn까지 오일러 피 함수 구하기: O(nloglogn)O(n \log log{n}) 계산 복잡도

만약 11부터 nn까지의 모든 수에 대해 오일러 피 함수값이 필요하다면, 각각의 수 nn개를 모두 소인수분해하는 것은 효율적이지 않다. 에라토스테네스의 체에서 썼던 아이디어를 적용할 수 있다. 위에서 보인 성질을 사용할 것이지만, 각각의 수에 대해 각 소인수마다 중간 결과를 갱신하는 대신, 우선 모든 소수를 찾은 후 각 소수에 대해 그로 나누어 떨어지는 모든 수에 대해 중간 결과를 갱신하는 방법을 사용한다.

이러한 접근은 근본적으로 에라토스테네스의 체와 같기 때문에, 계산 복잡도 또한 O(nloglogn)O(n \log \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;

    for (int i = 2; i <= n; i++) {
        if (phi[i] == i) {
            for (int j = i; j <= n; j += i)
                phi[j] -= phi[j] / i;
        }
    }
}

약수 합 성질

이 흥미로운 성질은 가우스에 의해 확립되었다:

_dnϕ(d)=n\sum\_{d|n} \phi{(d)} = n

합은 nn의 모든 약수 dd에 대해 더한 것이다.

예를 들어, 10의 약수는 1, 2, 5, 10이다. ϕ(1)+ϕ(2)+ϕ(5)+ϕ(10)=1+1+4+4=10\phi(1) + \phi(2) + \phi(5) + \phi(10) = 1 + 1 + 4 + 4 = 10이 성립한다.

약수 합 성질을 이용하여 1부터 nn까지 오일러 피 함수값을 계산하기

약수 합 성질은 1부터 nn까지의 모든 오일러 피 함수값을 계산하는데 사용될 수 있다. 이 구현은 에라토스테네스의 체를 이용한 이전 구현보다 약간 더 간단하지만, 약간 더 나쁜 계산 복잡도인 O(nlogn)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];
}

오일러 정리에서의 응용

오일러 피 함수의 가장 유명하고 중요한 성질은 오일러 정리에서 나타난다. aamm이 서로소일때, 다음 등식이 성립한다.

aϕ(m)1(modm)a^{\phi(m)} \equiv 1 \pmod m

mm이 소수인 경우, 오일러 정리는 페르마의 소정리가 된다:

am11(modm)a^{m - 1} \equiv 1 \pmod m

오일러 정리와 오일러 피 함수는 잉여 역수를 계산하는 데 활용되는 등 실전에서 자주 등장한다.

직접적인 결과로써 다음 합동식이 성립한다:

ananmodϕ(m)(modm)a^n \equiv a^{n \bmod \phi(m)} \pmod m

이 합동식을 통해 아주 큰 nn에 대해 xnmodmx^n \bmod m을 계산할 수 있다. 특히 nn이 다른 계산의 결과인 경우, nn을 법 ϕ(m)\phi(m)에 대해 계산하여도 된다.

일반화

서로소가 아닌 xx, mm에 대해 xnmodmx^n \bmod m을 효율적으로 계산하는 마지막 합동식의 잘 알려지지 않은 변형이 있다. 임의의 x,mx, mnlog2mn \geq \log_2 m에 대해 다음 합동식이 성립한다.

xnxϕ(m)+[nmodϕ(m)]modmx^{n}\equiv x^{\phi(m)+[n \bmod \phi(m)]} \mod m

증명:

p1,,ptp_1, \dots, p_txxmm 공통 소인수라고 하고, kik_ipip_imm을 나누는 횟수라고 하자. a=p1k1ptkta = p_1^{k_1} \dots p_t^{k_t}으로 정의하면 ma\frac{m}{a}xx는 서로소이다. aaxkx^k를 나누도록 하는 정수 중 가장 작은 것을 kk라고 하자. nkn \ge k를 가정하면 다음과 같이 쓸 수 있다:

xnmodm=xkaaxnkmodm=xka(axnkmodm)modm=xka(axnkmodama)modm=xkaa(xnkmodma)modm=xk(xnkmodma)modm\begin{align*}x^n \bmod m &= \frac{x^k}{a}ax^{n-k}\bmod m \\ &= \frac{x^k}{a}\left(ax^{n-k}\bmod m\right) \bmod m \\ &= \frac{x^k}{a}\left(ax^{n-k}\bmod a \frac{m}{a}\right) \bmod m \\ &=\frac{x^k}{a} a \left(x^{n-k} \bmod \frac{m}{a}\right)\bmod m \\ &= x^k\left(x^{n-k} \bmod \frac{m}{a}\right)\bmod m \end{align*}

세번째 줄과 네번째 줄 사이의 합동은 abmodac=a(bmodc)ab \bmod ac = a(b \bmod c)이므로 성립한다. b=cd+rb = cd + r, r<cr < c일 때, ab=acd+arab = acd + ar, ar<acar < ac임을 관찰하자.

xxma\frac{m}{a}는 서로소이므로, 오일러 정리를 사용하여 다음 공식을 얻는다.

xnmodm=xk(xnkmodϕ(ma)modma)modm.x^n \bmod m = x^k\left(x^{n-k \bmod \phi(\frac{m}{a})} \bmod \frac{m}{a}\right)\bmod m.

kk가 아주 작으므로 (사실 klog2mk \le \log_2 m이다) 이는 효율적으로 계산할 수 있다.

이 공식은 적용하기 어렵지만, xnmodmx^n \bmod m의 행동을 분석하기 위해 사용할 수 있다. 거듭제곱으로 이루어진 수열 (x1modm,x2modm,x3modm,)(x^1 \bmod m, x^2 \bmod m, x^3 \bmod m, \dots)은 첫 kk개의 항 이후 길이 ϕ(ma)\phi\left(\frac{m}{a}\right)인 순환에 진입한다. aa, ma\frac{m}{a}는 서로소이므로 ϕ(a)ϕ(ma)=ϕ(m)\phi(a) \cdot \phi\left(\frac{m}{a}\right) = \phi(m)를 얻는다. 특히, ϕ(m)\phi(m)ϕ(ma)\phi\left(\frac{m}{a}\right)으로 나누어 떨어진다. 따라서 이 순환이 길이 ϕ(m)\phi(m)를 갖는다고 말할 수도 있다. ϕ(m)log2mk\phi(m) \ge \log_2 m \ge k으로부터, 목표했던 더욱 간단한 공식을 얻는다:

xnxϕ(m)x(nϕ(m))modϕ(m)modmxϕ(m)+[nmodϕ(m)]modm.x^n \equiv x^{\phi(m)} x^{(n - \phi(m)) \bmod \phi(m)} \bmod m \equiv x^{\phi(m)+[n \bmod \phi(m)]} \mod m.

연습문제


Profile picture

소프트웨어 개발자 권도현입니다. 문제해결을 좋아합니다.
Email: sylvesterkwon@gmail.com

© 2024, Sylvester Kwon