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

在数学上,斐波那契数列是以递归的方法来定义

……续上回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数的快速算法已经写了4种,算法时间复杂度皆为O(log n),但常数项不同。

矩阵快速幂解法每步中两个大整数乘法是8次

快速平方的矩阵解法每步中两个大整数乘法是5次

二分解法每步中两个大整数乘法是3次

二进制模幂解法每步中两个大整数乘法是2次

有趣,刚好是个Fibonacci数列

欢迎点赞、留言

未完待续……

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容