Processing math: 0%
 

이항정리를 이용한 거듭제곱의 합 1^k+2^k+3^k+...+n^k 을 구하는 방법

1. 이항정리(binomial theorem)

 

0이상의 정수 n과 음이 아닌 정수 x,y에 대하여, (x+y)^{n} = \sum_{k = 0}^{n}\binom{n}{k}x^{k}y^{n-k}

 

https://en.wikipedia.org/wiki/Binomial_theorem

 

Binomial theorem - Wikipedia

From Wikipedia, the free encyclopedia Algebraic expansion of powers of a binomial In elementary algebra, the binomial theorem (or binomial expansion) describes the algebraic expansion of powers of a binomial. According to the theorem, it is possible to exp

en.wikipedia.org

 

y = 1로 두면 다음과 같은 식을 얻는다.

 

(x+1)^{n} = \sum_{k=0}^{n} \binom{n}{k} x^{k}

 

 

2. 점화식 유도

 

이항정리로부터, (m+1)^{k+1} = \sum_{x = 0}^{k+1} \binom{k+1}{x} m^{x}

 

우변에 x = k+1인 경우를 제외하면,

 

(m+1)^{k+1} - m^{k+1} = \sum_{x=0}^{k} \binom{k+1}{x}m^{x}

 

이제 양변에 m = 1부터 n까지 sigma를 취하면,

 

\sum_{m = 1}^{n} ((m+1)^{k+1} - m^{k+1}) = \sum_{m=1}^{n} \sum_{x=0}^{k} \binom{k+1}{x}m^{x}

 

좌변은 전개하면 망원급수형태인데,

 

2^{k+1} - 1^{k+1} + 3^{k+1} - 2^{k+1} + ... +n^{k+1} - (n-1)^{k+1} + (n+1)^{k+1} - n^{k} = (n+1)^{k+1} - 1

 

우변에 대해 정리를 해보자면, "푸비니의 정리"는 다중적분의 순서를 바꿀 수 있다는 정리이다.

 

이에 따라 우변의 두 이산합의 순서를 바꿔보면...

 

https://en.wikipedia.org/wiki/Fubini%27s_theorem

 

Fubini's theorem - Wikipedia

From Wikipedia, the free encyclopedia Conditions for switching order of integration in calculus In mathematical analysis, Fubini's theorem is a result that gives conditions under which it is possible to compute a double integral by using an iterated integr

en.wikipedia.org

 

\sum_{m=1}^{n} \sum_{x=0}^{k} \binom{k+1}{x}m^{x} = \sum_{x=0}^{k} \sum_{m=1}^{n} \binom{k+1}{x}m^{x}

 

이항계수 부분은 m과 무관하므로,

 

\sum_{x = 0}^{k} \binom{k+1}{x} (\sum_{m = 1}^{n} m^{x})

 

S_{x}(n) = 1^{x} + 2^{x} + ... + n^{x} = \sum_{m = 1}^{n} m^{x}라고 정의하자. 

 

모든 결과를 종합하면 다음 식을 얻는다.

 

(n+1)^{k+1} - 1 = \sum_{x = 0}^{k} \binom{k+1}{x} S_{x}(n)

 

 

3. 연습문제

 

25974번: 거듭제곱의 합 1 (acmicpc.net)

 

25974번: 거듭제곱의 합 1

1부터 n까지 모든 자연수의 p 거듭제곱의 합을 10^9+7로 나눈 나머지를 구하시오. 다시 말해, \left(\sum_{k=1}^{n}k^p\right)\mod\left(10^9+7\right) 을 구하시오.

www.acmicpc.net

 

 

4. 풀이

 

n과 k가 주어질때, S_{k}(n) = 1^{k} + 2^{k} + ... + n^{k}10^{9} + 7로 나눈 나머지를 구하는 문제.

 

위에서 유도한 (n+1)^{k+1} - 1 = \sum_{x = 0}^{k} \binom{k+1}{x} S_{x}(n)을 이용하면 문제를 풀 수 있을 것 같다.

 

n은 일정하니까, x = 0,1,2,...,k에 대하여 S_{x}(n)을 다이나믹 프로그래밍을 활용하여 차례대로 구한다.

 

i번째 S_{i}(n)을 구해놓는다면, 점화식을 이용해서 S_{i+1}(n)을 구할 수 있을 것 같다.

 

결국에 문제는 이항계수를 구하는게 문제인데, k!까지 모든 팩토리얼을 계산해놓고, (k!)^{-1}까지 구해놓는다.

 

def mod_factorial(p,mod):
    
    factorial = [0]*(p+2)
    factorial[0] = 1

    for i in range(1,p+2):
    
        factorial[i] = factorial[i-1]*i
        factorial[i] %= mod
    
    return factorial

 

 

팩토리얼 역원을 구하는 방법이 정리가 안되어있네..? (나중에 정리할 것 같지만.. 여기선 간단하게)

 

매우 큰 소수 p = 10^{9} + 7에 대하여, n이 최대 10^{9}까지니까, 모든 n!은 p와 서로소이다.

 

그래서 p에 대한 모듈로 곱셈의 역원이 존재하는데 페르마의 소정리로부터,

 

(n!)^{p-1} ≡ 1 (mod p)

 

n!을 빼보면, n! * (n!)^{p-2} ≡ 1 (mod p)

 

그러면, n!^{-1} ≡ (n!)^{p-2} (mod p)

 

그런데, 식을 살짝만 더 바꿔보면 다음과 같은 점화식을 얻는다.

 

(n-1)! * (n * (n!)^{p-2}) ≡ 1 (mod p)

 

즉, (n-1)!의 역원은 n!의 역원에 n을 곱하면 된다.

 

그러므로, inverse[n-1] = inverse[n] * n이라는 점화식을 얻는다.

 

이 점화식을 이용하면, 가장 큰 k에 대하여 inverse[k]는 (k!)^{p-2} = (factorial[k])^{p-2}로 구해놓고,

 

다이나믹 프로그래밍을 이용해서 거꾸로 순회해서 구하면 되겠다.

 

#(n)!*(n!)^(p-2) == 1
#(n-1)!*n*(n!)^(p-2) == 1
def mod_inv_factorial(factorial,p,mod):
    
    inverse = [0]*(p+2)

    inverse[p+1] = pow(factorial[p+1],mod-2,mod)

    for i in range(p+1,0,-1):
        
        inverse[i-1] = inverse[i]*i
        inverse[i-1] %= mod
    
    return inverse

 

factorial과 inverse를 모두 구해놓는다면, 이항계수를 소수 p로 나눈 나머지는..

 

\binom{n}{r} ≡ factorial[n] * inverse[r] * inverse[n-r] (mod p)로 구할 수 있을 것이다.

 

S[x]를 S_{x}(n)을 계산해놓은 값을 저장한다고 하자.

 

S[0]는 1^{0} + 2^{0} + ... + n^{0} = n이다.

 

이제 i는 1부터 p까지 순회하여, 배열을 채워넣는다.

 

점화식 (n+1)^{i+1} - 1 = \sum_{x=0}^{i} \binom{i+1}{x} S_{x}(n)을 이용하자.

 

매 반복문마다 구해야하는 값은 S_{i}(n)

 

그러면, (n+1)^{i+1} - 1 - \sum_{x=0}^{i-1} \binom{i+1}{x} S_{x}(n) = \binom{i+1}{i} S_{i}(n)

 

0~i-1까지 S배열에 S_{x}(n)의 값이 이미 계산되어 있고,

 

factorial, inverse 배열을 이용하면, 모든 \binom{i+1}{x}를 계산할 수 있다.

 

또한 (n+1)^{i+1}은 분할정복을 이용한 거듭제곱으로 빠르게 계산 가능하다.

 

좌변은 모두 계산가능하고, 다음 우변에 이항계수 \binom{i+1}{i}가 문제인데, .. 이제 보니까.. 

 

\binom{i+1}{i} = i+1이네...??

 

그런데, i+1 < p \geq 1000이므로, 10^{9} + 7과 서로소이다. 

 

그러므로 모듈로 곱셈 역원이 존재하고, 페르마의 소정리에 의해 (i+1)^{10^{9}+7-2}로 구할 수 있다.

 

따라서, 좌변을 모두 계산한 다음, (i+1)^{10^{9}+7-2}를 곱하면, S_{i}(n)을 얻게된다.

 

i = 1부터 p까지 반복하면, 구하고자 하는 값 S_{p}(n) = 1^{p} + 2^{p} + ... + n^{p}은 S[p]에 들어있다.

 

#(n+1)^(p+1) - 1 = sigma(t=0)^(p) (p+1Ct)St(n)
#nCr = n!/(r!)*(n-r)!
def sum_of_power(factorial,inverse,n,p):
    
    S = [0]*(p+1)

    S[0] = n % mod

    for i in range(1,p+1):
        
        sigma = 0

        #sum(j = 0)^(i-1) (i+1)Cj*Sj(n)
        for j in range(i+1):
            
            sigma += (S[j] * factorial[i+1]*inverse[j]*inverse[i+1-j])
            sigma %= mod

        #Si(n) = ((n+1)^(i+1)-1 - sigma)*(i+1)Ci^(-1)

        S[i] = pow(n+1,i+1,mod) -1 - sigma
        
        S[i] %= mod

        S[i] *= pow(i+1,mod-2,mod)

        S[i] %= mod
    
    return S[p]

n,p = map(int,input().split())

mod = 10**9+7

factorial = mod_factorial(p,mod)
inverse = mod_inv_factorial(factorial,p,mod)

print(sum_of_power(factorial,inverse,n,p))

 

놀랍게도, 최대 10^{9}인 n과는 전혀 무관하게 최대 1000인 p에 대하여 O(p^{2})으로 문제를 해결할 수 있다.

 

당연히 합동식 성질을 이용해 mod로 계속 나눠줘서 수를 압축해주는건 필수다.

728x90