斐波那契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列,指的是这样一个数列:
递推公式
通项公式
通项公式推导
此问题的线性递推数列属于二阶线性递推数列(n+2 - n = 2)
由上可知,斐波那契数列递推公式的特征方程为:
解得
则 ,
,
斐波那契数列的性质
与黄金分割的关系
有趣的是,这样一个完全是自然数的数列,通项公式却是用无理数来表达的。而且当 n 趋向于无穷大时,前一项与后一项的比值越来越逼近黄金分割 0.618(或者说后一项与前一项的比值小数部分越来越逼近 0.618)
平方与前后项关系
从第二项开始,每个偶数项的平方都比前后两项之积少 1,每个奇数项的平方都比前后两项之积多 1,也就是:
或
两倍项的关系
与集合子集的关系
斐波那契数列的第n+2项同时也代表了集合中所有不不包含相邻正整数的子集个数。
比如,n = 3, 第n+2项 为 5,此时 中所有不不包含相邻正整数的子集为 空集、{1}、{2}、{3}、{1,3}
项求和性质
斐波那契数列值的快速求法
其中[x]表示取距离x最近的整数。
数学问题
1. 有一段楼梯有10 级台阶,规定每一步只能跨一级或两级,要登上第10 级台阶有几种不同的走法?
首先考虑登上第一级有一种走法({1}),登上第二级种走法({1,1},{2}),我们知道,由于最后一步同样只能跨一级或者两级,登上第三阶有两种情况,
- 最后一步跨一级
- 最后一步跨两级
所以它的走法数就等于两种情况相加,也就是前两级的走法数相加()。
2. 一枚均匀的硬币掷10次,问不连续出现正面的可能情形有多少种?
3. 求递推数列的通项公式?
4. 一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?
和台阶问题类似,从一开始是新出生的一对小兔子,可得:
可见,从第三个月兔子具有繁殖能力开始,因为成兔对数,等于上月的成兔对数+上月的幼仔对数(是由上上月的成兔生的,经过两个月变成了成兔)。所以,当月的幼兔对数 等于 上月的成兔对数,也就等于上上月的兔子对数,再加 上月的兔子对数 就是当月的兔子对数。是符合斐波那契数列的性质的。
斐波那契数列的矩阵乘法定义
设矩阵A=第一行(1,1)第二行(1,0) 迭代n次可以得到:
F(n+ 1) = (0,1)* A^(F(2),F(1))T = (0,1)* A^( n ) * (1,1)T
题目描述
递归算法和代码实现
由斐波那契数列从第三项开始,每一项等于前两项之和,可以使用递归计算给定整数的斐波那契数。
算法
C++版本:
class Solution {
public:
int fib(int N) {
if (N <= 1) {
return N;
}
return fib(N-1) + fib(N-2);
}
};
python版本:
class Solution:
def fib(self, N: int) -> int:
if N <= 1:
return N
return self.fib(N-1) + self.fib(N-2)
复杂度分析
时间复杂度:O(2^N)。从下面的fib(5) 的递归树容易知道,递归的层数为N,fib( )函数的调用次数 = 1 + 2 + 4 + ... + 2^( N - 2) = 2^( N - 1 ) - 1,所以是指数级别时间的。
空间复杂度:O(N)。因为在堆栈需要存储 fib(N) 的函数调用,因此需要与 N 成正比的空间大小。随着堆栈的不断增长,如果没有足够的内存则会导致 StackOverflowError。
记忆化自底向上算法和代码实现
通过迭代计算斐波那契数的子问题并存储已计算的值,通过已计算的值进行计算。减少递归带来的重复计算。
算法
- 若N 小于等于 1,return N。
- 初始化数组迭代 N,将计算出的答案存储在数组中。
- 使用数组前面的两个斐波那契数(fib(0) ,fib(1))计算当前的斐波那契数。
- 直达计算到 N,则 return 它的斐波那契数。
这个算法思路和动态规划的思路相同,因为斐波那契数列问题是典型的一维动态规划问题:
若新建长度为 n 的 dp 列表,则空间复杂度为 O(N) 。而由于 dp 列表第 i项只与第 i-1 和第 i-2项有关,只需要初始化三个整形变量,利用辅助变量使两个初始变量交替前进即可,从而将空间复杂度降至 O(1)
C++版本:
class Solution {
public:
int fib(int N) {
vector<int> res;
res.push_back(0);
res.push_back(1);
for (int i = 1; i < N; ++i){
res.push_back(res[i] + res[i - 1]);
}
return res[N];
}
}
python 版本:
class Solution:
def fib(self, N: int) -> int:
temp = [0,1]
if N >= 2:
for i in range(2,N+1):
temp[i%2] = temp[0] + temp[1]
return temp[N%2]
补充:python中不存在大数越界的问题。
复杂度分析
时间复杂度:O(N)。迭代次数为N,线性时间。
空间复杂度:O(N),使用了空间大小为 N
的容器数组。或者优化为只用一个大小为2的数组(和使用三个临时变量原理一样),不断迭代替换这两个值(python版本 : 根据N%2判断输出的是cache[0]还是cache[1])。
矩阵求幂和代码实现
从之前的斐波那契数列数列求解可知斐波那契数列矩阵方程:
算法
python 版本:
class Solution:
def fib(self, N: int) -> int:
if (N <= 1):
return N
A = [[1, 1], [1, 0]]
self.matrix_power(A, N-1)
return A[0][0]
def matrix_power(self, A: list, N: int):
if (N <= 1):
return A
self.matrix_power(A, N//2)
self.multiply(A, A)
B = [[1, 1], [1, 0]]
if (N%2 != 0):
self.multiply(A, B)
def multiply(self, A: list, B: list):
x = A[0][0] * B[0][0] + A[0][1] * B[1][0]
y = A[0][0] * B[0][1] + A[0][1] * B[1][1]
z = A[1][0] * B[0][0] + A[1][1] * B[1][0]
w = A[1][0] * B[0][1] + A[1][1] * B[1][1]
A[0][0] = x
A[0][1] = y
A[1][0] = z
A[1][1] = w
C++版本可以用vector<vector<int>>来存储二维矩阵,其他思路类似。
复杂度分析
时间复杂度:O(log N)
空间复杂度:O(log N)。matrixPower
函数递归时堆栈使用的空间。
公式法和代码实现
斐波那契通项公式:
算法
使用黄金分割率(通项公式)计算第 N
个斐波那契数。
C++版本:
class Solution {
public:
int fib(int N) {
double goldenRatio = (sqrt(5) - 1) / 2;
return (int)((pow((1+goldenRatio),N) -pow(-goldenRatio, N)) / sqrt(5));
}
}
python 版本:
class Solution:
def fib(N):
golden_ratio = (5 ** 0.5 - 1) / 2
return int(((1+golden_ratio)**N -(-golden_ratio)** N) / 5 ** 0.5)
复杂度分析
时间复杂度:O(1)。常数的时间复杂度,因为我们是基于斐波那契通项公式进行计算,没有使用循环或递归。
空间复杂度:O(1)。存储黄金分割率所使用的空间。