Angka Fibonacci yang digeneralisasi dalam O (log N)

Dalam pekerjaan kursus, diharuskan menulis algoritma dengan kompleksitas logaritmik, yang akan menemukan angka ke-N dari deret Fibonacci.





Algoritma

Menemukan beberapa artikel tentang topik ini, semuanya berhubungan dengan deret klasik bilangan Fibonacci. Baginya, Anda bisa menerapkan rumus ini:





Tetapi dalam pekerjaan saya, seri umum digunakan, di mana dua angka pertama adalah nol dan beberapa parameter. Setelah berjam-jam mencari, saya menemukan komentar menarik di mana penulis memberikan rumus untuk temuan siklik dari setiap urutan berulang linier (termasuk serangkaian angka Fibonacci).





Di sini Q adalah matriks 2x2 yang elemen-elemennya dapat dihitung dengan mudah.





Mengganti beberapa bilangan Fibonacci, kita menemukan bahwa matriks Q = [[0,1], [1,1]].





Rumus akhir dari matriks, yang berisi angka ke-N dari deret Fibonacci umum, dapat ditulis sebagai berikut:





Fn adalah bilangan Fibonacci yang diinginkan,

C adalah kuncinya,

n adalah nomor urut dari nomor tersebut





Jelasnya, untuk efisiensi seluruh algoritme, perlu menggunakan algoritme tercepat untuk menaikkan matriks Q ke pangkat n. Artikel ini membantu saya mengatasinya . Ini menjelaskan bahwa untuk menaikkan matriks ke pangkat n, Anda dapat membagi n menjadi pangkat dua dan kemudian menaikkan matriks hanya ke pangkat ini. Pendekatan ini memberikan kompleksitas O (log N).





Kemudian tinggal mengalikan dengan matriks [[C, C], [C, 0]] dan mendapatkan elemen dengan indeks [0,1].





Implementasi Python

class FiboMatrix:
    key = 0
    matrix_cur = [[0,0], [0,0]]
    matrix_one = [[0,1], [1,1]]

    def __init__(self, key):
        self.key = key
        self.matrix_cur = [[key, key], [key, 0]]
        self.PowsBuffer = {}
        #       

    def MultiplyMatrix(self, M1, M2):
        """ 
          2x2   ."""

        a11 = M1[0][0] * M2[0][0] + M1[0][1] * M2[1][0]
        a12 = M1[0][0] * M2[0][1] + M1[0][1] * M2[1][1]
        a21 = M1[1][0] * M2[0][0] + M1[1][1] * M2[1][0]
        a22 = M1[1][0] * M2[0][1] + M1[1][1] * M2[1][1]
        return [[a11, a12], [a21, a22]]

    def PowMatrix(self, M: list, p: int):
        """   .
          2x2     .
        """

        if p == 1:
            return M
        if p in self.PowsBuffer:
            return self.PowsBuffer[p]

        K = self.PowMatrix(M, int(p / 2))
        R = self.MultiplyMatrix(K, K)
        self.PowsBuffer[p] = R
        return R

    def GetNum(self, n):
        if n == 0:
            return 0
        if n == 1:
            return 1
        
        #      n   
        powers = []
        p = 0
        for digit in reversed(bin(n)[2:]):
            if digit == '1':
                powers.append(int(pow(2, p)))
            p += 1

        matrices = [self.PowMatrix(FiboMatrix.matrix_one, p)
                    for p in powers]

        #     

        while len(matrices) > 1:
            M1 = matrices.pop()
            M2 = matrices.pop()
            R = self.MultiplyMatrix(M1, M2)
            #   
            matrices.append(R)

        self.matrix_cur = self.MultiplyMatrix(self.matrix_cur, matrices[0])
        #    F1  F2  ,    
        return self.matrix_cur[0][1]
      
      



Untuk membandingkan efisiensi, analog paling sederhana dari algoritma ini ditulis menggunakan loop:





def Fib(num, key):
    fib_1 = 0
    fib_2 = key

    for dec in range(num):
        fib_1, fib_2 = fib_2, fib_1+fib_2

    return fib_2
      
      



Tolok ukur mengkonfirmasi harapan kami: algoritme klasik membutuhkan waktu beberapa kali lebih banyak pada angka ke-10.000.








All Articles