[C++] Obliczanie N-tej liczby Fibonacciego modulo

xiikzodz
Użytkownik
Użytkownik
Posty: 1874
Rejestracja: 4 paź 2008, o 02:13
Płeć: Kobieta
Lokalizacja: Lost Hope
Podziękował: 28 razy
Pomógł: 502 razy

[C++] Obliczanie N-tej liczby Fibonacciego modulo

Post autor: xiikzodz »

Potęgowanie macierzy oczywiście jest znacznie fajniejsze i, co zabawne, dokładnie takie zadanie udało mi się kiedyś wymyślić dla studentów na kursie krypto przy okazji metody iterowanego kwadratu. Ale po zerknięciu na oryginalne rozwiązanie to rekurencyjne wydawało mi się pewną poprawą i akurat w ciągu tamtych kilku sekund nie przypomniało mi się inne.
Przy koszcie pamięciowym rzędu \(\displaystyle{ \mathcal{O}(\log n)}\) i jakiś tam modyfikacjach może pewnie działać w czasie \(\displaystyle{ \mathcal{O}(\log n)}\), ale szkoda zachodu. Albo zostawić koszt liniowy, albo poświęcić 5 minut na opanowanie potęgowania macierzy. Wcale nie trzeba rozumieć macierzy. Wystarczy rozumieć mnożenie macierzy i iterowany kwadrat, a to bardzo elementarne. Mam teraz nieco więcej czasu więc może coś dopiszę:

Idea jest następująca, jeśli \(\displaystyle{ A}\) jest tą macierzą opisującą ciąg Fibonacciego to, jak to wypisał Zordon, wystarczy poznać wartość \(\displaystyle{ A^n}\), żeby otrzymać \(\displaystyle{ F_{n+1}}\). Zapiszmy pomocniczo liczbę \(\displaystyle{ n}\) w systemie dwójkowym, czyli przedstawmy ją w postaci:

\(\displaystyle{ n=\sum a_i2^i}\). Powiedzmy, że otrzymaliśmy \(\displaystyle{ \color{red}11101}\).

Wówczas

\(\displaystyle{ A^n=A^{\begin{color}{red}1\end{color}\cdot 2^4}\cdot A^{\begin{color}{red}1\end{color}\cdot 2^3}\cdot A^{\begin{color}{red}1\end{color}\cdot 2^2}\cdot A^{\begin{color}{red}0\end{color}\cdot 2^1}\cdot A^{\begin{color}{red}1\end{color}\cdot 2^0}}\)

i widzimy, że wystarczy czterokrotnie (a w ogólności \(\displaystyle{ \mathcal{O}(\log n)}\) krotnie) podnieść jakąś potęgę \(\displaystyle{ A}\) do kwadratu, żeby otrzymać wszystkie czynniki w iloczynie powyżej.

Stąd algorytm podnoszenia \(\displaystyle{ A}\) do potęgi \(\displaystyle{ n}\)-tej w "pseudo C":

Input: \(\displaystyle{ n, A=\begin{pmatrix}1&1\\1&0\end{pmatrix}}\)
Output: \(\displaystyle{ A^n}\)

\(\displaystyle{ E}\) to macierz jednostkowa, czyli w przypadku \(\displaystyle{ 2\times 2}\) macierz \(\displaystyle{ \begin{pmatrix}1&0\\0&1\end{pmatrix}}\)

Kod: Zaznacz cały

long F(long n, matrix A){
X = E;
Y = A;
i = n;
while (i > 0){
    if (i % 2) X = X * Y;
    Y = Y^2;
    i = floor(i/2);
};
return X;}
Pozostaje zaimplementować mnożenie (modulo) macierzy \(\displaystyle{ 2\times 2}\) którymi w algorytmie powyżej są E,A,X,Y (i ewentulanie przeładować operator * w klasie matrix, którą trzeba dopisać). Liczba \(\displaystyle{ F_{n+1}}\) stoi w lewym górnym rogu macierzy \(\displaystyle{ A^n}\).

Działający kod w Matlabie. Matlab nie rozróżnia mnożenia macierzy od mnożenia liczb (zbrodnią jest kodowanie macierzy w innym środowisku), więc jest krótko:

Kod: Zaznacz cały

M=10^9+7;
E=[1,0;0,1];
A=[1,1;1,0];
X = E;
Y = A;
i = n;
while (i > 0){
    if mod(i,2) X = mod(X * Y,M);
    end;
    Y = mod(Y^2,M);
    i = floor(i/2);
end;
Xitami

[C++] Obliczanie N-tej liczby Fibonacciego modulo

Post autor: Xitami »

w PARI/GP wygląda to tak
modfibo(n,mod)=lift(Mod([1,1;1,0],n)^mod)[1,2]
ODPOWIEDZ