뤼카 정리
뤼카 정리는 이항계수를 구하는 정리이다.
이항 계수를 구하는 일반적인 방법들
우리가 흔히 이항계수를 구하는 방법은 다음과 같다.
(kn)=(kn−1)+(k−1n−1)
이 방법은 O(n2) 의 시간복잡도를 갖는다.
두 번째는 팩토리얼과 모듈러 역원을 이용하는 방법이다.
(kn)=k!(n−k)!n! 이기 때문에 O(n)으로 n! 까지만 팩토리얼을 전처리 해두면 모듈러 역원을 이용해 n!⋅k!−1⋅(n−k)!−1(modm) 과 같이 구할 수 있다.
이 방법의 시간복잡도는 O(n) 정도라고 볼 수 있다.
뤼카 정리의 이점
뤼카 정리는 다음과 같은 이항 계수를 빠른 시간복잡도로 구할 수 있다.
(kn)(modp),p∈소수
시간 복잡도는 전처리에 O(p2) 혹은 O(plogp) 이며 (kn)의 값을 얻어오는데는 O(nlogpn) 정도가 걸린다.
따라서 BOJ 11402 - 이항 계수 4같은 문제를 해결할 수 있다.
이 문제를 보면 N≤1018 이고 M≤2,000 에 M이 소수이기 때문에 뤼카 정리를 쓰라고 만든 문제이다.
공식
뤼카 정리의 공식은 다음과 같다.
n과 k를 p진수로 나타낸 꼴이 다음과 같을 때,
nk=nq⋅pq+nq−1⋅pq−1+⋯+n0⋅p0(0≤ni<p)=kq⋅pq+kq−1⋅pq−1+⋯+k0⋅p0(0≤ki<p)
(kn)≡i=0∏q(kini)(modp)
구현
증명보다 구현을 먼저 살펴보자.
우선 O(p2)에 (pp) 까지 전처리를 해둔다 하자.
ll n, k, p;
cin >> n >> k >> p;
vector<vector<int>> binom(p + 1, vector<int>(p + 1));
for (int i = 0; i <= p; i++)
for (int j = 0; j <= i; j++)
binom[i][j] = !i || !j ? 1 : (binom[i - 1][j] + binom[i - 1][j - 1]) % p;
이제 n,k를 0이 아닐때까지 p로 나눠주며 (k % pn % p)(modp)를 정답에 곱해주면 된다.
ll ret = 1;
while (n || k) {
ret = (ret * binom[n % p][k % p]) % p;
n /= p, k /= p;
}
cout << ret;
구현은 굉장히 간단하다.
BOJ 11402 - 이항 계수 4 를 푸는 전체 코드는 다음과 같다.
void solve() {
ll n, k, p;
cin >> n >> k >> p;
vector<vector<int>> binom(p + 1, vector<int>(p + 1));
for (int i = 0; i <= p; i++)
for (int j = 0; j <= i; j++)
binom[i][j] = !i || !j ? 1 : (binom[i - 1][j] + binom[i - 1][j - 1]) % p;
ll ret = 1;
while (n || k) {
ret = (ret * binom[n % p][k % p]) % p;
n /= p, k /= p;
}
cout << ret;
}
증명
Lemma 1.
(1+x)pn≡1+xpn(modp)
증명
(np)=n!(p−n)!p!=n(n−1)⋯2⋅1p(p−1)⋯(p−n+1)
p는 소수이므로 p∣(np)
분자 p가 분모에서 나눠질 수 없기 때문이다. n=p and n=0 임을 가정한다.
이항정리에 의해
(1+x)p=i=0∑p(ip)xi=(0p)x0+(1p)x1+⋯+(pp)xp
양 끝을 제외한 중간의 항들이 항상 p로 나누어 떨어지기 때문에
(1+x)p≡1+xp(modp)
비슷하게 (1+x)pn과 1+xpn 에 대해서도 다음과 같이 유도할 수 있다.
(1+x)pn≡1+xpn(modp)
□
뤼카 정리
다음과 같은 꼴을 보자.
nk=nq⋅pq+nq−1⋅pq−1+⋯+n0⋅p0(0≤ni<p)=kq⋅pq+kq−1⋅pq−1+⋯+k0⋅p0(0≤ki<p)
다음과 같은 이항정리 식을 고려한다.
k=0∑n(kn)xk=(1+x)n=(1+x)nq⋅pq+nq−1⋅pq−1+⋯+n0⋅p0=i=0∏q[(1+x)pi]ni
Lemma 1에 의해
i=0∏q[(1+x)pi]ni≡i=0∏q[1+xpi]ni≡(1+xpq)nq⋅(1+xpq−1)nq−1⋯(1+xp0)n0(modp)
위 식을 전개했을 때 차수가 k인 xk가 되기 위해서 xpi(0≤i≤q) 가 곱해진 횟수들을 고려한다.
이 각각의 횟수들을 ki (0≤i≤m, 0≤ki≤ni) 라고할 때,
k=kq⋅pq+kq−1⋅pq−1+⋯+k0⋅p0 를 만족해야 한다.
차수가 k가 되기 위해서 xpq 가 곱해지는 횟수를 kq 라고 할 때, 곱셈에서 차수의 결과는 차수끼리 더한것과 같기 때문에 위와 같은 식이 나왔다.
그럼 xk의 계수는 i=0∏k(kini) 가 되는데, 이항정리에 의해 [1+xpi]ni=j=0∑ni(jni)(xpi)j⋅1ni−j 의 꼴에서 ki개를 고르는 조합의 가지수들을 모두 곱해주었기 때문이다.
결론적으로
i=0∏q[1+xpi]ni≡(1+xpq)nq⋅(1+xpq−1)nq−1⋯(1+xp0)n0≡k=0∑ni=0∏q(kini)xk(modp)
xk의 계수를 고려했을 때 원래 이항계수에서 (kn) 이지만 법 p에 대해 새로운 꼴에선 i=0∏q(kini) 꼴이 되었으므로
(kn)≡i=0∏q(kini)(modp)(0≤ni<p,0≤ki≤ni)
■
참고
Comments