1. 키타마사 법(kitamasa method)
수열 an의 점화식을 이전의 몇개 항으로 정의한다면, 귀납적 정의, 재귀적 수열 등으로 부른다.
an=k∑i=1wian−i
이런 형태로 정의되는 대표적인 수열은 피보나치 수열이다.
an=an−1+an−2,w1=w2=1,k=2
이 피보나치 수열의 가장 빠른? 해법중 하나는 행렬을 이용하는 방법이다.
https://deepdata.tistory.com/760
행렬을 이용한 피보나치 수열 문제의 해법
1. 피보나치 수열의 행렬 표현 피보나치 수열의 점화식은 다음과 같다. an+1=an+an−1 an=an+0 따라서 행렬로 나타내면 다음과 같다 $$(an+1an)=\begin{pmatrix}
deepdata.tistory.com
일반적으로 an=∑ki=1an−iwi는 다음과 같은 행렬 표현으로 바꿀 수 있다.

이 방법의 시간복잡도는 O(k3logN)으로 알려져있다.
이 방법도 나름 의미있지만, k = 1000정도 일때 세제곱이다보니 상당히 느려진다..
키타마사법의 목표는 an을 O(k2logN)에 구하는 것이다.
2. 귀납적 수열의 n번항은 항상 1~k번 항의 선형결합으로 나타낼 수 있다
핵심 아이디어는 "이전의 k개의 항으로 결정되는" 수열의 점화식 an=∑ki=1wian−i에 대하여,
an은 항상 "최초 k개의 항" a1,a2,...,ak의 선형 결합으로 나타낼 수 있다는 것이다.
an=k∑i=1aici
이 말이 무슨 말이냐면, 피보나치 수열 an=an−1+an−2에 대하여,
a3=a1+a2,c1=c2=1
a4=a3+a2=a1+2a2,c1=1,c2=2
a5=a4+a3=a1+2a2+a1+a2=2a1+3a2,c1=2,c2=3
...
이렇게 이전에 구한 항을 이용해서, n-1,n-2,... 항들을 소거하는 방식으로 항상 ci를 찾을 수 있다.
만약에 수열 ci를 빠르게 찾을 수 있다면, 원하는 an을 빠르게 구할 수 있을 것이다.
3. 다항식의 나눗셈으로의 접근
여러 블로그들을 찾아 읽어봤지만, 가장 쉬운 접근은 다항식의 나눗셈으로 접근하는 것 같다..
https://justicehui.github.io/hard-algorithm/2021/03/13/kitamasa/
키타마사법(Kitamasa Method, きたまさ法)
서론 키타마사법(Kitamasa Method, きたまさ法)은 AN=c1AN−1+c2AN−2+⋯+cKAN−K=∑Ki=1ciAN−i와 같은 선형 점화식의 N번째 항을 O(K2logN), 더 나아가 O(KlogKlogN)에 계산하는
justicehui.github.io
https://milkclouds.work/linear-recurrence/
Linear Recurrence, 키타마사법
다음과 같이 정의되는 무한수열 an이 있다고 하자. an=c1an−1+c2an−2+…+ckan−k c와 a의 k개의 초항이 주어졌을 때 an을 빠르게 구하는 게 하고 싶은 일이다. 1. 행렬곱 행
milkclouds.work
an의 점화식이 n이 k+1이상인 모든 자연수에 대해 다음과 같이 주어지면,
an=w1an−1+w2an−2+...+wkan−k
여기서 재귀적으로 an−1=w1an−2+w2an−3+...+wkan−1−k을 대입하면서 항을 소거하는 과정이
an=xn으로 치환해서, 다항식처럼 생각한 다음
xn=w1xn−1+w2xn−2+...+wkxn−k
이것을 xk−w1xk−1−w2xk−2−...−wkx0으로 나눈 나머지를 구하는 과정과 동일하다는 것이다.
일반적인 식으로 해보면... 이게 되는건지 안되는건지 어려우니까 예를 들어 생각해보자.
가장 간단한 피보나치 수열 an=an−1+an−2의 n = 5에 대해 생각해보자.
a5=a4+a3=(a3+a2)+a3=2a3+a2
a5=2a3+a2=2(a2+a1)+a2=3a2+2a1
a5=3a2+2a1=3(a1+a0)+2a1=5a1+3a0
만약 an=xn라 하고, a2−a1−1=x2−x−1로 나눠보면...
x5=(x3+x2+2x+3)(x2−x−1)+5x+3이다.
나머지 5x+3이 a5=5a1+3a0와 동일하다.
그러므로, an을 구하는 문제는 f(x)=xk−w1xk−1−...−wkx0에 대하여, xnmodf(x)를 구하는 문제가 된다.
4. 구현 예시
곱셈과 나눗셈을 기본적인 구현 방법으로 구현하면, 항 수가 K개일때, O(K2)에 할 수 있다.
https://deepdata.tistory.com/981
다항식의 곱셈과 나눗셈 기본 컴퓨터 구현 방법 배우기
1. 다항식의 곱셈 두 다항식의 곱셈은 구현하는 방법이 많이 있지만... 당장은 어려우니 일단 O(k2)으로 naive하게 구현 해보자. 다항식은 각 항의 계수를 배열에 저장하면 되는데 $f(x) = 2x^{2} + x
deepdata.tistory.com
또한 N이 매우 크면 단순한 곱셈으로는 xn을 구하는데 시간이 매우 오래걸린다.
하지만 분할 정복을 이용한 거듭제곱을 이용하면 O(logN)에 구할 수 있다.
이때, xnmodf(x)를 구하는게 목표인데,
xn을 먼저 구할려고 배열로 [0,0,0,..........,1]로 만들면 공간 메모리 초과날것 같다
합동식의 성질 a*b mod p = (a mod p * b mod p) mod p로
거듭제곱 x,x2,x4,...을 구하면서 나머지를 구하면 된다.
또한 우리는 다항식의 나눗셈을 하면서 나머지만 필요로 한다. .. 몫은 return하지 않도록 한다.
전체 시간 복잡도가 O(K2logN)에 해결할 수 있다.
#naive polynomial multiplication
def multiply(a,b,mod):
result = [0]*(len(a)+len(b)-1)
for i in range(len(a)):
for j in range(len(b)):
result[i+j] += (a[i]*b[j]) % mod
return result
#negative mod
def negative_mod(x,mod):
if x < 0:
return (x+mod)%mod
else:
return x%mod
#naive polynomial division
#a/b
def divide(a,b,mod):
q = [0]*(len(a)-len(b)+1) #몫
#최고차항부터 순회해서..
for i in range(len(q)-1,-1,-1):
q[i] = a[i+len(b)-1]//b[-1]
for j in range(len(b)-1,-1,-1):
a[i+j] -= (q[i]*b[j])
a[i+j] = negative_mod(a[i+j],mod)
while a:
x = a.pop()
if x != 0:
a.append(x)
break
return a
def kitamasa(w,a,n,mod):
c = [1] #a_n = sum(w[i]a_n-i) = sum(c[i]a[i])
x = [0,1] #x^1, x^2, x^4, ...
#w를 이용해 f(x) = x^k - w1x^k-1 - w2x^k-2 - ...을 구한다.
f = [negative_mod(-i,mod) for i in w]
f.append(1)
#분할정복을 이용한 거듭제곱으로 x^n mod f를 구한다.
#합동식의 성질 a*b mod p = (a mod p * b mod p) mod p를 이용
while n:
if n & 1:
c = divide(multiply(c,x,mod),f,mod)
n >>= 1
x = divide(multiply(x,x,mod),f,mod)
#c배열을 찾았다면, an의 값을 구한다.
#find a_n = sum(c[i]a[i])
answer = 0
for i in range(len(a)):
answer += (a[i]*c[i])
answer = negative_mod(answer,mod)
return answer
5. 연습문제
11444번: 피보나치 수 6 (acmicpc.net)
11444번: 피보나치 수 6
첫째 줄에 n이 주어진다. n은 1,000,000,000,000,000,000보다 작거나 같은 자연수이다.
www.acmicpc.net
w = [1,1]이고 a = [0,1]인 경우에 대한 키타마사법을 이용하면 해결할 수 있다.
FFT를 이용한 고속 곱셈을 하거나, 나눗셈도 빠른 나눗셈을 사용하지 않는 이상... 당장 실전성은 없는듯하다...
혹은 최신 알고리즘으로 보스턴 모리 알고리즘을 쓰지 않는 이상은... 필요가 없는것 같다
'대수학' 카테고리의 다른 글
컴퓨터가 실수를 연분수 표현(continued fraction)으로 나타내는 방법 (0) | 2023.09.20 |
---|---|
Fast Fourier Transform을 이용한 빠른 다항식 곱셈 공부하기 (0) | 2023.08.29 |
다항식의 곱셈과 나눗셈 기본 컴퓨터 구현 방법 배우기 (0) | 2023.08.26 |