【每日算法】NC65 斐波那契数列2

520 阅读4分钟

这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战

前言

前面我们写了动态规划地去求斐波那契数列,本篇文章会讲解写出时间复杂度为 log(n) 的斐波那契数列解法所需要的知识点。

再试

线性代数

线性代数怎么来的?

这肯定就要说一下线性方程组啦,那什么是线性方程组呢?

{ a1x1 + a2x2 + a3x3 = b1,

a4x4 + a5x5 + a6x6 = b2,

.... }

就是类似这种的方程组,线性方程就是其中的一行。

方程组越写越长,越写越多,于是干脆就把线性方程组简写成了矩阵

|

a1 a2 a3

a4 a5 a6

|

这里写出来的效果不太好,同学们可以自行百度搜索一下。

矩阵的出现就帮助我们计算更加复杂算法,让一代代大学生都倒在线性代数下。

大学里学的线性代数都差不多还给老师了,这里就不误人子弟了,这里参考的链接放在文章最末尾了,感兴趣的同学可以去看一下,这里我们只需要知道我们即将用到矩阵就行。

本文需要使用到矩阵的乘法运算,运算如下:

|

|a1 , a2|,

|a3 , a4|

| × |

|b1 , b2|,

|b3 , b4|

| = |

|a1 b1 + a2 b3 , a1 b2 + a2 b4|,

|a3 b1 + a4 b3 , a3 b2 + a4 b4||

二阶常系数齐次线性递推数列

斐波那契数列 属于 二阶常系数齐次线性递推数列。

二阶常系数齐次线性递推数列是什么?

二阶常系数齐次线性递推数列就是满足an = c1a(n-1) + c2a(n-2),其中 c1,c2 为常数,这里的()内的数为下标。

好了,如果是二阶常系数齐次线性递推数列,那就会存在一个二阶的矩阵,可以满足这个数列的规律:|F(n) F(n-1)| = |F(n-1) F(n-2)| * A。A 是一个矩阵。

不懂也没关系,我们可以类比成 X 的 N 次方,我们可以通过移动比特位的方式去降低时间复杂度了,而不是一次次地相乘。

时间复杂度降低到log(n)的秘诀

我们来写一个 X 的 N 次方。

先是用普通的 for 循环来写一遍:

private long calculationPower(long base, long power) {
    long result = 1;
    for (; power != 0; power--) {
        result *= base;
    }
    return result;
}

如果是 2 的 100 次方,那就要运行 100 次,时间复杂度为 n。

当我们优化成通过移动比特位的方式:

private long calculationPower1(long base, long power) {
    long result = 1;
    long temp = base;
    for (; power != 0; power = power >> 1) {
        if ((power & 1) != 0) {
            result *= temp;
        }
        temp *= temp;
    }
    return result;
}

代码都是可以运行的,结果准确无误。

100 的二进制等于 1100100,也就是说,本来要循环 100 次,现在只需要循环 7 次!

效率提升了 14 倍!

我刚学的时候也有一点不懂。

  1. 为啥 temp 在每次循环中都要自乘?
  2. 为啥在(power & 1) != 0时才result *= temp;

第一点,因为每次 power 右移的时候,其实是在给我们的次方数除于 2,temp 存储的是当前比特位是 X 的 n 次方的结果。最直观的例子就是拿二进制来看一下,6 =110,这三位二进制数分别代表 4 2 1,代入到 2 ^ 6 中,到二进制110中最前面的 1 时,2 =10,result = 4,就是才乘了两次,剩下的这个 1 就代表这 2 ^ 4,而这时的 temp 在上一次循环中做了一次自乘(4 * 4),已经达到了 2 ^ 4,所以 temp 要自乘。

第二点,就跟转换二进制到十进制一样,肯定是当前比特位为 1 才能算是有啊。

(第一点写的我心力憔悴,愣是想了很久才表达出来,个人感觉只要举得例子能看懂就行,其他的可能我个人表达也有问题。)

最后

本篇我们已经明白了怎么把时间复杂度从 O(n) 减少到 O(log(n))的原理,但我们要写出时间复杂度为O(log(n))的解法还需要做一件事情。

未完待续。。。

这里是程序员徐小白,【每日算法】是我新开的一个专栏,在这里主要记录我学习算法的日常,也希望我能够坚持每日学习算法,不知道这样的文章风格您是否喜欢,不要吝啬您免费的赞,您的点赞、收藏以及评论都是我下班后坚持更文的动力。