美文网首页程序猿阵线联盟-汇总各类技术干货程序员
Fibonacci数列高效解法大全及时间复杂度分析 连载【8】

Fibonacci数列高效解法大全及时间复杂度分析 连载【8】

作者: FSS_Sosei | 来源:发表于2018-10-18 17:28 被阅读209次
    在数学上,斐波那契数列是以递归的方法来定义

    ……续上回Fibonacci数列高效解法大全及时间复杂度分析 连载【7】

    之前的篇章把各种Fibonacci数列的基本算法讨论过了

    那么是否可以做到更快呢,有什么加速手段

    这篇来说下

    首先第一个手段是改进算法的加速

    16.  快速平方的矩阵解法

    Fibonacci数列高效解法大全及时间复杂度分析 连载【5】这篇里说过矩阵解法

    矩阵法虽然跟二进制模幂解法时间复杂度一样,可算第100万项斐波那契数用时是二进制模幂解法的10倍。这是因为这算法的时间常数项大

    里面用到了矩阵乘法,通用矩阵乘法算法的时间复杂度是阶数n的O(n^3)。也就是对一个二阶矩阵,分解步骤中有8次乘法,非常耗时,造成矩阵解法时间常数项很大

    之前程序是直接用的numpy库做矩阵乘法,numpy库里就是通用矩阵乘法算法

    然而,对斐波那契数的矩阵解法,只需对二阶矩阵做运算。而其中的二阶矩阵平方步骤是有更小时间复杂度算法的

    二阶矩阵快速平法算法的时间复杂度为阶数2的O(2^(log2(5)))

    也就是二阶矩阵一次平方运算分解步骤中只用做5次乘法。比通用矩阵算法的8次降低了不少

    但是,对斐波那契数矩阵解法而言,矩阵乘法步骤是其中的时间常数项,缩短矩阵乘法用时只是降低整个算法的常数项,可并不降低整个算法的时间复杂度

    下面就是我写的程序

    import numpy

    import gmpy2

    def fast_power_of_second_order_matrix(input_matrix: 'matrix', exponential: int) -> 'matrix':

        assert isinstance(exponential, int), 'Exponential is an error of non-integer type.'

        assert exponential >= 0, 'The exponent cannot be negative.'

        assert input_matrix.shape == (2, 2), 'It can only be a second-order matrix'

        if numpy.min_scalar_type(input_matrix) in (numpy.dtype(bool), numpy.dtype(int), numpy.dtype(float), numpy.dtype(complex)):

            output_matrix = numpy.asmatrix(numpy.identity(2, dtype = numpy.min_scalar_type(input_matrix)))  #根据输入矩阵的标量类型,把output_matrix初始化为相同类型的单位矩阵

        elif numpy.issubsctype(input_matrix, numpy.dtype(object)):

            for matrix_element in input_matrix.flat:

                if type(matrix_element) not in (type(gmpy2.mpz()), type(gmpy2.xmpz()), type(gmpy2.mpq()), type(gmpy2.mpfr()), type(gmpy2.mpc()), int, float):

                    raise TypeError('The matrix can only be of Boolean or numeric type.')

            output_matrix = numpy.mat(((gmpy2.mpz(1), gmpy2.mpz(0)), (gmpy2.mpz(0), gmpy2.mpz(1))))  #初始化值为mpz类型的单位矩阵

        else:

            raise TypeError('The matrix can only be of Boolean or numeric type.')

        def square_of_second_order_matrix(input_second_order_matrix: 'matrix') -> 'matrix':

            output_second_order_matrix = numpy.copy(input_second_order_matrix)

            sum_of_main_diagonal = output_second_order_matrix[0, 0] + output_second_order_matrix[1, 1]

            product_of_antidiagonal = output_second_order_matrix[0, 1] * output_second_order_matrix[1, 0]

            output_second_order_matrix[0, 0] **= 2

            output_second_order_matrix[0, 0] += product_of_antidiagonal

            output_second_order_matrix[0, 1] *= sum_of_main_diagonal

            output_second_order_matrix[1, 0] *= sum_of_main_diagonal

            output_second_order_matrix[1, 1] **= 2

            output_second_order_matrix[1, 1] += product_of_antidiagonal

            return output_second_order_matrix

        intermediate_variable = numpy.copy(input_matrix)

        while True:

            if exponential & 1 == 1:

                output_matrix *= intermediate_variable

                if exponential == 1: break

            intermediate_variable = square_of_second_order_matrix(intermediate_variable)

            exponential >>= 1

        return output_matrix

    import numpy.matlib

    from gmpy2 import mpz

    def Fibonacci_sequence_06 (n: int) -> int:  #参数n是表示求第n项Fibonacci数

        '返回单项的矩阵解法'

        assert isinstance(n, int), 'n is an error of non-integer type.'

        Use_threshold_of_fast_power = 100000

        if n >= 2:

            fib_matrix = numpy.mat(((mpz(1), mpz(1)), (mpz(1), mpz(0))))

            if n > Use_threshold_of_fast_power:

                fib_matrix = fast_power_of_second_order_matrix(fib_matrix, n - 1)

            else:

                fib_matrix **= n - 1

            return fib_matrix[0,0]

        elif n == 1:

            return 1

        elif n == 0:

            return 0

        else:

            return None

    Fibonacci_sequence_06(1000000)

    用算到第100万项Fibonacci数来测量下用时

    Total time: 0.036093秒

    比之前用通用矩阵乘法库的程序缩短了15%的时间

    注意,那么一大段程序是有基础开销的

    所以在非大数的时候用这个反而会不如通用库快

    怎么办呢

    加一个阈值判别。当N大于阈值之后,才用这个二阶矩阵快速平法算法,数不大时就调用通用库

    搞定

    欢迎点赞、留言

    未完待续……

    相关文章

      网友评论

        本文标题:Fibonacci数列高效解法大全及时间复杂度分析 连载【8】

        本文链接:https://www.haomeiwen.com/subject/mrurzftx.html