Problem
Definiujemy Gn=⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧011Gn−1+Gn−2+Gn−3if n=0if n=1if n=2if n≥3 Jest to ciąg generowany rekursywnie na podstawie ziarna (seed-a) w postaci trzech pierwszych wyrazów.
Concept (bottom-up)
Analogicznie jest w przypadku ciągu Fibonacciego, który definiujemy następująco: Fn=⎩⎪⎪⎨⎪⎪⎧01Fn−1+Fn−2if n=0if n=1oth.
Jednakże możemy zapisać ciąg Fibonacciego w sposób macierzowy: [1110]n=[Fn+1FnFnFn−1] [Fn+1FnFnFn−1]×[1110]=[Fn+2Fn+1Fn+1Fn]
Dla naszego problemu Gn również musimy znaleźć odpowiednią macierz, którą po podniesieniu do odpowiedniej potęgi uzyskamy dostęp do n-tego wyrazu ciągu G.
Rozwiązanie
Musimy znaleźć taką macierz A, że: ⎣⎢⎡GnGn−1Gn−2⎦⎥⎤=A×⎣⎢⎡Gn−1Gn−2Gn−3⎦⎥⎤
Czyli, żeby otrzymać rekurencję Gn=Gn−1+Gn−2+Gn−3 pierwszy wiersz A musi wynosić A1=[ 1 1 1 ].
W przypadku wierszy A2 oraz A3 wystarczy „przepisać” wartości Gn−1 oraz Gn−2. Więc wystarczy zrobić prostą mapę bitową przenoszącą te wartości do macierzy po prawej stronie. Dlatego też: A=⎣⎢⎡110101100⎦⎥⎤
⎣⎢⎡110101100⎦⎥⎤2=⎣⎢⎡211210110⎦⎥⎤ ⎣⎢⎡110101100⎦⎥⎤3=⎣⎢⎡421321211⎦⎥⎤ i ogólnie ⎣⎢⎡110101100⎦⎥⎤n=⎣⎢⎡GnGn−1Gn−2Gn−1+Gn−2Gn−2+Gn−3Gn−3+Gn−4Gn−1Gn−2Gn−3⎦⎥⎤ przy czym, żeby nie mieć problemu z określeniem liczb dla ujemnych indeksów (np. co to jest Gn−4 kiedy mamy n=3 ?) ustalimy macierz początkową X=⎣⎢⎡110101100⎦⎥⎤3=⎣⎢⎡421321211⎦⎥⎤=⎣⎢⎡G4G3G2G3+G2G2+G1G1+G0G3G2G1⎦⎥⎤ którą będziemy przemnażać przez macierz A celem uzyskania kolejnych wyrazów ciągu Gn.
W takim układzie mamy na samym początku już policzone wyrazy od G0 do G4 a następne wyrazy będziemy uzyskiwali przez mnożenie macierzy X przez A.
Teraz, problemem jest osiągnięcie złożoności obliczeniowej T(n)=O(lgn) przy przemnażaniu macierzy X przez A.
Należy zauważyć, że w ogólnym przypadku nie ma przemienności w mnożeniu macierzy. Tutaj jednak, operujemy na potęgach tak naprawdę jednej macierzy co daje nam przemienność.
Przy przemnażaniu naszej „macierzy atomowej” A nie należy się też martwić o złożoność, ponieważ za każdym razem jest to macierz o 3 kolumnach i 3 wierszach.
Można użyć algorytmu analogicznego do liczenia potęg liczb naturalnych w czasie O(lgn).
Nasz algorytm działałby następująco:
- Jeśli n∈{0,…,4} zwróć zapisaną wartość.
- W przeciwnym wypadku oblicz n−4 potęgę macierzy A przy pomocy poniższej funkcji
power_matrix
, pomnóż wynik przez X i go zwróć.
Liczenie potęgi macierzy w czasie O(lgn)
Zdefiniujemy funkcję PowerMatrix
(A,k):
output =
⎣⎢⎡100010001⎦⎥⎤
while
k>0:
if
2∣ k:
output *=
A
- k=⌊2k⌋
- A=A2
return output