8-1 计算第n个斐波那契数
斐波那契数(Fibonacci Number)
斐波那契数列(Fibonacci Sequence)是数学中最著名的数列之一,由意大利数学家比萨的莱昂纳多(Leonardo of Pisa),又名斐波那契(Fibonacci)在 1202 年的著作《计算之书》中提出。
数列的定义如下:
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2) (n >= 2)
数列的前几项为:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
斐波那契数列在自然界和计算机科学中有着广泛的应用:
- 黄金比例(Golden Ratio):相邻两项的比值趋近于黄金比例 φ ≈ 1.618
- 自然界:向日葵的螺旋排列、贝壳的形状、松果的鳞片数
- 算法分析:作为动态规划(Dynamic Programming)的经典入门问题
- 数据结构:斐波那契堆(Fibonacci Heap)的名称来源
计算第 n 个斐波那契数有多种方法,从最朴素的递归到高效的矩阵快速幂,各有优劣。
递归法
递归法(Recursive Approach)是斐波那契数列定义的直接翻译:将 F(n) 表示为 F(n-1) + F(n-2),直到到达基准情形 F(0) = 0 和 F(1) = 1。
这种方法直观简洁,但存在严重的效率问题:计算 F(n) 时,F(n-1) 和 F(n-2) 各自会产生大量重复计算。以计算 F(5) 为例:
F(5)
/ \
F(4) F(3)
/ \ / \
F(3) F(2) F(2) F(1)
/ \ / \ / \
F(2) F(1) F(1)F(0) F(1)F(0)
/ \
F(1) F(0)
可以看到 F(3) 被计算了 2 次,F(2) 被计算了 3 次。随着 n 的增大,重复计算的次数呈指数级增长,时间复杂度为 O(2^n)。
C++ 实现
#include <iostream>
int fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// recursive case
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main()
{
int n = 10;
std::cout << "F(" << n << ") = " << fibonacci(n) << "\n";
return 0;
}
运行该程序将输出
F(10) = 55
函数 fibonacci 直接按照数学定义递归求解。当 n 较小时非常直观,但 n 超过 40 后运行时间会明显变慢,因为存在大量重复计算。
C 实现
#include <stdio.h>
int fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// recursive case
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main()
{
int n = 10;
printf("F(%d) = %d\n", n, fibonacci(n));
return 0;
}
运行该程序将输出
F(10) = 55
C 语言版本的递归实现逻辑与 C++ 完全一致,仅输出方式不同。
Python 实现
def fibonacci(n):
# base cases
if n == 0:
return 0
if n == 1:
return 1
# recursive case
return fibonacci(n - 1) + fibonacci(n - 2)
n = 10
print(f"F({n}) = {fibonacci(n)}")
运行该程序将输出
F(10) = 55
Python 的递归实现更加简洁,且整数没有溢出问题,但默认递归深度限制为 1000 层,可通过 sys.setrecursionlimit() 调整。
Go 实现
package main
import "fmt"
func fibonacci(n int) int {
// base cases
if n == 0 {
return 0
}
if n == 1 {
return 1
}
// recursive case
return fibonacci(n-1) + fibonacci(n-2)
}
func main() {
n := 10
fmt.Printf("F(%d) = %d\n", n, fibonacci(n))
}
运行该程序将输出
F(10) = 55
Go 语言版本的递归逻辑与其他语言一致。注意 Go 的 if 语句条件不需要括号。
记忆化递归(Memoization)
记忆化递归(Memoization)是对朴素递归的优化:将已经计算过的结果存储起来,下次需要时直接查表,避免重复计算。这是一种自顶向下(Top-down)的动态规划方法。
核心思路:
- 使用一个数组或哈希表作为"备忘录"(Memo),记录每个 F(i) 的结果。
- 计算 F(n) 前,先查备忘录是否已有结果。
- 如果有,直接返回;如果没有,递归计算后存入备忘录。
时间复杂度优化到 O(n),空间复杂度为 O(n)。
C++ 实现
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
long long memo[100];
long long fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// check memo: already computed
if (memo[n] != -1) return memo[n];
// compute and store in memo
memo[n] = fibonacci(n - 1) + fibonacci(n - 2);
return memo[n];
}
int main()
{
// initialize memo with -1 (not computed)
memset(memo, -1, sizeof(memo));
int n = 10;
cout << "F(" << n << ") = " << fibonacci(n) << "\n";
// test larger value
memset(memo, -1, sizeof(memo));
int m = 50;
cout << "F(" << m << ") = " << fibonacci(m) << "\n";
return 0;
}
运行该程序将输出
F(10) = 55
F(50) = 12586269025
使用 memo 数组存储已计算的结果。memset 将数组初始化为 -1 表示尚未计算。通过记忆化,F(50) 可以在瞬间计算完成,而朴素递归几乎无法在合理时间内完成。
C 实现
#include <stdio.h>
#include <string.h>
long long memo[100];
long long fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// check memo: already computed
if (memo[n] != -1) return memo[n];
// compute and store in memo
memo[n] = fibonacci(n - 1) + fibonacci(n - 2);
return memo[n];
}
int main()
{
// initialize memo with -1 (not computed)
memset(memo, -1, sizeof(memo));
int n = 10;
printf("F(%d) = %lld\n", n, fibonacci(n));
// test larger value
memset(memo, -1, sizeof(memo));
int m = 50;
printf("F(%d) = %lld\n", m, fibonacci(m));
return 0;
}
运行该程序将输出
F(10) = 55
F(50) = 12586269025
C 语言版本使用全局数组 memo 和 memset 初始化,注意 F(50) 的结果超过 32 位整数范围,需使用 long long 和 %lld 格式说明符。
Python 实现
def fibonacci(n, memo=None):
if memo is None:
memo = {}
# base cases
if n == 0:
return 0
if n == 1:
return 1
# check memo: already computed
if n in memo:
return memo[n]
# compute and store in memo
memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
return memo[n]
n = 10
print(f"F({n}) = {fibonacci(n)}")
m = 50
print(f"F({m}) = {fibonacci(m)}")
运行该程序将输出
F(10) = 55
F(50) = 12586269025
Python 使用字典作为备忘录,通过 memo=None 避免默认参数的可变对象陷阱。Python 的整数自动支持任意精度,因此无需担心溢出。
Go 实现
package main
import "fmt"
func fibonacci(n int, memo map[int]int64) int64 {
// base cases
if n == 0 {
return 0
}
if n == 1 {
return 1
}
// check memo: already computed
if val, ok := memo[n]; ok {
return val
}
// compute and store in memo
memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo)
return memo[n]
}
func main() {
n := 10
fmt.Printf("F(%d) = %d\n", n, fibonacci(n, make(map[int]int64)))
m := 50
fmt.Printf("F(%d) = %d\n", m, fibonacci(m, make(map[int]int64)))
}
运行该程序将输出
F(10) = 55
F(50) = 12586269025
Go 语言使用 map[int]int64 作为备忘录,通过逗号 ok 模式(val, ok := memo[n])判断键是否存在。使用 int64 确保 64 位精度。
迭代法
迭代法(Iterative Approach)采用自底向上(Bottom-up)的策略:从已知的 F(0) 和 F(1) 出发,逐步向前计算,直到得到 F(n)。这是一种直观且高效的动态规划方法。
基本思路:
- 创建数组
dp,其中dp[0] = 0,dp[1] = 1。 - 对 i 从 2 到 n,计算
dp[i] = dp[i-1] + dp[i-2]。 - 最终
dp[n]即为所求。
时间复杂度 O(n),空间复杂度 O(n)。
C++ 实现
#include <iostream>
#include <vector>
using namespace std;
long long fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// dp array to store results
vector<long long> dp(n + 1);
dp[0] = 0;
dp[1] = 1;
// fill dp table bottom-up
for (int i = 2; i <= n; i++)
{
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
int main()
{
int n = 10;
cout << "F(" << n << ") = " << fibonacci(n) << "\n";
return 0;
}
运行该程序将输出
F(10) = 55
使用 vector<long long> 作为 dp 数组,从 F(2) 开始依次填充,避免了递归带来的重复计算和栈开销。
C 实现
#include <stdio.h>
#include <stdlib.h>
long long fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// allocate dp array
long long *dp = (long long *)malloc((n + 1) * sizeof(long long));
dp[0] = 0;
dp[1] = 1;
// fill dp table bottom-up
for (int i = 2; i <= n; i++)
{
dp[i] = dp[i - 1] + dp[i - 2];
}
long long result = dp[n];
free(dp);
return result;
}
int main()
{
int n = 10;
printf("F(%d) = %lld\n", n, fibonacci(n));
return 0;
}
运行该程序将输出
F(10) = 55
C 语言使用 malloc 动态分配 dp 数组,使用后通过 free 释放内存,避免内存泄漏。
Python 实现
def fibonacci(n):
# base cases
if n == 0:
return 0
if n == 1:
return 1
# dp list to store results
dp = [0] * (n + 1)
dp[0] = 0
dp[1] = 1
# fill dp table bottom-up
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
n = 10
print(f"F({n}) = {fibonacci(n)}")
运行该程序将输出
F(10) = 55
Python 使用列表(list)存储中间结果,语法简洁明了。列表的乘法操作 [0] * (n + 1) 可快速创建指定长度的数组。
Go 实现
package main
import "fmt"
func fibonacci(n int) int64 {
// base cases
if n == 0 {
return 0
}
if n == 1 {
return 1
}
// dp slice to store results
dp := make([]int64, n+1)
dp[0] = 0
dp[1] = 1
// fill dp table bottom-up
for i := 2; i <= n; i++ {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
func main() {
n := 10
fmt.Printf("F(%d) = %d\n", n, fibonacci(n))
}
运行该程序将输出
F(10) = 55
Go 语言使用切片(slice)make([]int64, n+1) 创建 dp 数组,语法清晰且自动管理内存。
空间优化迭代法
在迭代法中,我们实际上只需要前两个值就能计算当前值,整个 dp 数组并不是必需的。空间优化迭代法只维护两个变量来存储 F(n-1) 和 F(n-2),将空间复杂度从 O(n) 降低到 O(1)。
基本思路:
- 用变量
prev2表示 F(n-2),prev1表示 F(n-1)。 - 每次迭代计算
current = prev1 + prev2,然后更新prev2 = prev1,prev1 = current。 - 最终
prev1即为 F(n)。
时间复杂度 O(n),空间复杂度 O(1)。
C++ 实现
#include <iostream>
using namespace std;
long long fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// only keep previous two values
long long prev2 = 0; // F(n-2)
long long prev1 = 1; // F(n-1)
for (int i = 2; i <= n; i++)
{
long long current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}
int main()
{
int n = 10;
cout << "F(" << n << ") = " << fibonacci(n) << "\n";
return 0;
}
运行该程序将输出
F(10) = 55
仅需两个变量 prev2 和 prev1 即可完成计算,不需要任何额外数组,空间效率最优。
C 实现
#include <stdio.h>
long long fibonacci(int n)
{
// base cases
if (n == 0) return 0;
if (n == 1) return 1;
// only keep previous two values
long long prev2 = 0; // F(n-2)
long long prev1 = 1; // F(n-1)
for (int i = 2; i <= n; i++)
{
long long current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}
int main()
{
int n = 10;
printf("F(%d) = %lld\n", n, fibonacci(n));
return 0;
}
运行该程序将输出
F(10) = 55
C 语言版本的实现与 C++ 完全一致,仅输出函数不同。这是实际工程中最常用的斐波那契计算方法。
Python 实现
def fibonacci(n):
# base cases
if n == 0:
return 0
if n == 1:
return 1
# only keep previous two values
prev2 = 0 # F(n-2)
prev1 = 1 # F(n-1)
for i in range(2, n + 1):
current = prev1 + prev2
prev2 = prev1
prev1 = current
return prev1
n = 10
print(f"F({n}) = {fibonacci(n)}")
运行该程序将输出
F(10) = 55
Python 版本同样仅需两个变量,简洁高效。也可以使用 Python 的多元赋值进一步简化为 prev1, prev2 = prev1 + prev2, prev1。
Go 实现
package main
import "fmt"
func fibonacci(n int) int64 {
// base cases
if n == 0 {
return 0
}
if n == 1 {
return 1
}
// only keep previous two values
var prev2 int64 = 0 // F(n-2)
var prev1 int64 = 1 // F(n-1)
for i := 2; i <= n; i++ {
current := prev1 + prev2
prev2 = prev1
prev1 = current
}
return prev1
}
func main() {
n := 10
fmt.Printf("F(%d) = %d\n", n, fibonacci(n))
}
运行该程序将输出
F(10) = 55
Go 语言版本使用显式类型声明 var prev2 int64 = 0 确保使用 64 位整数,逻辑与其他语言完全一致。
矩阵快速幂法
矩阵快速幂法(Matrix Exponentiation)利用了斐波那契数列的矩阵表示性质,可以在 O(log n) 时间内计算出 F(n)。
数学原理:斐波那契数列可以表示为矩阵乘法:
| F(n+1) F(n) | | 1 1 | ^ n
| F(n) F(n-1) | = | 1 0 |
即:
| F(n+1) | | 1 1 | ^ n | 1 |
| F(n) | = | 1 0 | * | 0 |
计算矩阵的 n 次幂时,使用快速幂(Exponentiation by Squaring)算法:
若 n 为偶数: M^n = (M^(n/2))^2
若 n 为奇数: M^n = M * M^(n-1)
这样将 O(n) 的逐次乘法降为 O(log n) 的分治计算。
时间复杂度 O(log n),空间复杂度 O(log n)(递归栈)或 O(1)(迭代版本)。
C++ 实现
#include <iostream>
using namespace std;
// 2x2 matrix multiplication
void multiply(long long a[2][2], long long b[2][2])
{
long long x = a[0][0] * b[0][0] + a[0][1] * b[1][0];
long long y = a[0][0] * b[0][1] + a[0][1] * b[1][1];
long long z = a[1][0] * b[0][0] + a[1][1] * b[1][0];
long long 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;
}
// matrix exponentiation by squaring
void power(long long mat[2][2], int n)
{
if (n == 0 || n == 1) return;
long long base[2][2] = {{1, 1}, {1, 0}};
power(mat, n / 2);
multiply(mat, mat);
// if n is odd, multiply by base matrix
if (n % 2 != 0)
{
multiply(mat, base);
}
}
long long fibonacci(int n)
{
if (n == 0) return 0;
long long mat[2][2] = {{1, 1}, {1, 0}};
power(mat, n - 1);
return mat[0][0];
}
int main()
{
int n = 10;
cout << "F(" << n << ") = " << fibonacci(n) << "\n";
return 0;
}
运行该程序将输出
F(10) = 55
power 函数通过递归将矩阵幂运算分解为子问题:先计算 M^(n/2),再自乘得到 M^n。奇数时额外乘一次基础矩阵。这使得计算 F(n) 的时间从 O(n) 降到 O(log n)。
C 实现
#include <stdio.h>
// 2x2 matrix multiplication
void multiply(long long a[2][2], long long b[2][2])
{
long long x = a[0][0] * b[0][0] + a[0][1] * b[1][0];
long long y = a[0][0] * b[0][1] + a[0][1] * b[1][1];
long long z = a[1][0] * b[0][0] + a[1][1] * b[1][0];
long long 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;
}
// matrix exponentiation by squaring
void power(long long mat[2][2], int n)
{
if (n == 0 || n == 1) return;
long long base[2][2] = {{1, 1}, {1, 0}};
power(mat, n / 2);
multiply(mat, mat);
// if n is odd, multiply by base matrix
if (n % 2 != 0)
{
multiply(mat, base);
}
}
long long fibonacci(int n)
{
if (n == 0) return 0;
long long mat[2][2] = {{1, 1}, {1, 0}};
power(mat, n - 1);
return mat[0][0];
}
int main()
{
int n = 10;
printf("F(%d) = %lld\n", n, fibonacci(n));
return 0;
}
运行该程序将输出
F(10) = 55
C 语言版本的矩阵快速幂与 C++ 逻辑完全一致,使用二维数组表示矩阵,通过指针传递实现原地修改。
Python 实现
def multiply(a, b):
"""2x2 matrix multiplication"""
return [
[a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1]],
[a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1]]
]
def power(mat, n):
"""matrix exponentiation by squaring"""
if n == 0 or n == 1:
return mat
base = [[1, 1], [1, 0]]
# recursive squaring
mat = power(mat, n // 2)
mat = multiply(mat, mat)
# if n is odd, multiply by base matrix
if n % 2 != 0:
mat = multiply(mat, base)
return mat
def fibonacci(n):
if n == 0:
return 0
mat = [[1, 1], [1, 0]]
result = power(mat, n - 1)
return result[0][0]
n = 10
print(f"F({n}) = {fibonacci(n)}")
运行该程序将输出
F(10) = 55
Python 版本使用嵌套列表表示矩阵,multiply 函数返回新矩阵而非原地修改,函数式风格更加清晰。
Go 实现
package main
import "fmt"
// 2x2 matrix type
type Matrix [2][2]int64
// 2x2 matrix multiplication
func multiply(a, b Matrix) Matrix {
return Matrix{
{a[0][0]*b[0][0] + a[0][1]*b[1][0], a[0][0]*b[0][1] + a[0][1]*b[1][1]},
{a[1][0]*b[0][0] + a[1][1]*b[1][0], a[1][0]*b[0][1] + a[1][1]*b[1][1]},
}
}
// matrix exponentiation by squaring
func power(mat Matrix, n int) Matrix {
if n == 0 || n == 1 {
return mat
}
base := Matrix{{1, 1}, {1, 0}}
// recursive squaring
mat = power(mat, n/2)
mat = multiply(mat, mat)
// if n is odd, multiply by base matrix
if n%2 != 0 {
mat = multiply(mat, base)
}
return mat
}
func fibonacci(n int) int64 {
if n == 0 {
return 0
}
mat := Matrix{{1, 1}, {1, 0}}
result := power(mat, n-1)
return result[0][0]
}
func main() {
n := 10
fmt.Printf("F(%d) = %d\n", n, fibonacci(n))
}
运行该程序将输出
F(10) = 55
Go 语言版本定义了 Matrix 类型 [2][2]int64,multiply 函数返回新矩阵,代码结构清晰。Go 的类型系统使矩阵操作更加类型安全。
通项公式法(Binet's Formula)
斐波那契数列存在一个封闭形式的解析解,称为比奈公式(Binet's Formula):
F(n) = (φ^n - ψ^n) / √5
其中:
φ = (1 + √5) / 2 ≈ 1.6180339887... (黄金比例,Golden Ratio)
ψ = (1 - √5) / 2 ≈ -0.6180339887... (共轭黄金比例)
由于 |ψ| < 1,当 n 较大时 ψ^n 趋近于 0,因此 F(n) 实际上就是 φ^n / √5 四舍五入到最接近的整数。
该方法的时间复杂度为 O(1),但由于浮点数精度限制,当 n 较大时(通常 n > 70)会出现计算误差,因此仅适用于小规模计算或理论推导。
C++ 实现
#include <iostream>
#include <cmath>
using namespace std;
long long fibonacci(int n)
{
double sqrt5 = sqrt(5.0);
double phi = (1.0 + sqrt5) / 2.0; // golden ratio
double psi = (1.0 - sqrt5) / 2.0; // conjugate
double result = (pow(phi, n) - pow(psi, n)) / sqrt5;
return (long long)round(result);
}
int main()
{
int n = 10;
cout << "F(" << n << ") = " << fibonacci(n) << "\n";
return 0;
}
运行该程序将输出
F(10) = 55
使用 <cmath> 中的 sqrt、pow 和 round 函数计算比奈公式。round 确保浮点计算结果正确取整。
C 实现
#include <stdio.h>
#include <math.h>
long long fibonacci(int n)
{
double sqrt5 = sqrt(5.0);
double phi = (1.0 + sqrt5) / 2.0; // golden ratio
double psi = (1.0 - sqrt5) / 2.0; // conjugate
double result = (pow(phi, n) - pow(psi, n)) / sqrt5;
return (long long)round(result);
}
int main()
{
int n = 10;
printf("F(%d) = %lld\n", n, fibonacci(n));
return 0;
}
运行该程序将输出
F(10) = 55
C 语言版本使用 <math.h> 提供的数学函数,编译时需链接数学库(-lm)。
Python 实现
import math
def fibonacci(n):
sqrt5 = math.sqrt(5)
phi = (1 + sqrt5) / 2 # golden ratio
psi = (1 - sqrt5) / 2 # conjugate
result = (phi ** n - psi ** n) / sqrt5
return round(result)
n = 10
print(f"F({n}) = {fibonacci(n)}")
运行该程序将输出
F(10) = 55
Python 版本使用 math.sqrt 和幂运算符 ** 实现比奈公式,round 函数确保正确取整。
Go 实现
package main
import (
"fmt"
"math"
)
func fibonacci(n int) int64 {
sqrt5 := math.Sqrt(5)
phi := (1 + sqrt5) / 2 // golden ratio
psi := (1 - sqrt5) / 2 // conjugate
result := (math.Pow(phi, float64(n)) - math.Pow(psi, float64(n))) / sqrt5
return int64(math.Round(result))
}
func main() {
n := 10
fmt.Printf("F(%d) = %d\n", n, fibonacci(n))
}
运行该程序将输出
F(10) = 55
Go 语言版本使用 math 包中的函数,注意需要将 n 转换为 float64 类型以进行浮点运算。
完整对比
以下程序实现了所有方法,并对比它们在相同输入下的结果。通过对比不同方法的输出和耗时,可以直观地理解各方法的性能差异。
C++ 实现
#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
#include <chrono>
using namespace std;
using namespace std::chrono;
// Method 1: Simple Recursion
long long fib_recursive(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
return fib_recursive(n - 1) + fib_recursive(n - 2);
}
// Method 2: Memoization
long long fib_memo(int n, long long memo[])
{
if (n == 0) return 0;
if (n == 1) return 1;
if (memo[n] != -1) return memo[n];
memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo);
return memo[n];
}
// Method 3: Iterative with array
long long fib_iterative(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
vector<long long> dp(n + 1);
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; i++)
{
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// Method 4: Space-optimized iterative
long long fib_optimized(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
long long prev2 = 0, prev1 = 1;
for (int i = 2; i <= n; i++)
{
long long curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
// Method 5: Matrix exponentiation
void mat_multiply(long long a[2][2], long long b[2][2])
{
long long x = a[0][0] * b[0][0] + a[0][1] * b[1][0];
long long y = a[0][0] * b[0][1] + a[0][1] * b[1][1];
long long z = a[1][0] * b[0][0] + a[1][1] * b[1][0];
long long 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;
}
void mat_power(long long mat[2][2], int n)
{
if (n == 0 || n == 1) return;
long long base[2][2] = {{1, 1}, {1, 0}};
mat_power(mat, n / 2);
mat_multiply(mat, mat);
if (n % 2 != 0) mat_multiply(mat, base);
}
long long fib_matrix(int n)
{
if (n == 0) return 0;
long long mat[2][2] = {{1, 1}, {1, 0}};
mat_power(mat, n - 1);
return mat[0][0];
}
// Method 6: Binet's formula
long long fib_binet(int n)
{
double sqrt5 = sqrt(5.0);
double phi = (1.0 + sqrt5) / 2.0;
double psi = (1.0 - sqrt5) / 2.0;
return (long long)round((pow(phi, n) - pow(psi, n)) / sqrt5);
}
int main()
{
int test_cases[] = {0, 1, 10, 20};
long long memo[100];
cout << "=== Fibonacci Comparison ===" << "\n\n";
for (int n : test_cases)
{
cout << "F(" << n << "):" << "\n";
auto start = high_resolution_clock::now();
cout << " Recursive: " << fib_recursive(n);
auto end = high_resolution_clock::now();
cout << " (" << duration_cast<microseconds>(end - start).count() << " us)" << "\n";
memset(memo, -1, sizeof(memo));
start = high_resolution_clock::now();
cout << " Memoization: " << fib_memo(n, memo);
end = high_resolution_clock::now();
cout << " (" << duration_cast<microseconds>(end - start).count() << " us)" << "\n";
start = high_resolution_clock::now();
cout << " Iterative: " << fib_iterative(n);
end = high_resolution_clock::now();
cout << " (" << duration_cast<microseconds>(end - start).count() << " us)" << "\n";
start = high_resolution_clock::now();
cout << " Space-optimized: " << fib_optimized(n);
end = high_resolution_clock::now();
cout << " (" << duration_cast<microseconds>(end - start).count() << " us)" << "\n";
start = high_resolution_clock::now();
cout << " Matrix: " << fib_matrix(n);
end = high_resolution_clock::now();
cout << " (" << duration_cast<microseconds>(end - start).count() << " us)" << "\n";
start = high_resolution_clock::now();
cout << " Binet: " << fib_binet(n);
end = high_resolution_clock::now();
cout << " (" << duration_cast<microseconds>(end - start).count() << " us)" << "\n";
cout << "\n";
}
return 0;
}
运行该程序将输出
=== Fibonacci Comparison ===
F(0):
Recursive: 0 (0 us)
Memoization: 0 (0 us)
Iterative: 0 (0 us)
Space-optimized: 0 (0 us)
Matrix: 0 (0 us)
Binet: 0 (0 us)
F(1):
Recursive: 1 (0 us)
Memoization: 1 (0 us)
Iterative: 1 (0 us)
Space-optimized: 1 (0 us)
Matrix: 1 (0 us)
Binet: 1 (0 us)
F(10):
Recursive: 55 (5 us)
Memoization: 55 (2 us)
Iterative: 55 (1 us)
Space-optimized: 55 (0 us)
Matrix: 55 (3 us)
Binet: 55 (1 us)
F(20):
Recursive: 6765 (520 us)
Memoization: 6765 (3 us)
Iterative: 6765 (1 us)
Space-optimized: 6765 (0 us)
Matrix: 6765 (4 us)
Binet: 6765 (1 us)
可以看到所有方法在相同的 n 值下结果一致。递归法的耗时随 n 增大而急剧增长,而其他方法保持高效。
C 实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
// Method 1: Simple Recursion
long long fib_recursive(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
return fib_recursive(n - 1) + fib_recursive(n - 2);
}
// Method 2: Memoization
long long fib_memo(int n, long long memo[])
{
if (n == 0) return 0;
if (n == 1) return 1;
if (memo[n] != -1) return memo[n];
memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo);
return memo[n];
}
// Method 3: Iterative with array
long long fib_iterative(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
long long *dp = (long long *)malloc((n + 1) * sizeof(long long));
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; i++)
{
dp[i] = dp[i - 1] + dp[i - 2];
}
long long result = dp[n];
free(dp);
return result;
}
// Method 4: Space-optimized iterative
long long fib_optimized(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
long long prev2 = 0, prev1 = 1;
for (int i = 2; i <= n; i++)
{
long long curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
// Method 5: Matrix exponentiation
void mat_multiply(long long a[2][2], long long b[2][2])
{
long long x = a[0][0] * b[0][0] + a[0][1] * b[1][0];
long long y = a[0][0] * b[0][1] + a[0][1] * b[1][1];
long long z = a[1][0] * b[0][0] + a[1][1] * b[1][0];
long long 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;
}
void mat_power(long long mat[2][2], int n)
{
if (n == 0 || n == 1) return;
long long base[2][2] = {{1, 1}, {1, 0}};
mat_power(mat, n / 2);
mat_multiply(mat, mat);
if (n % 2 != 0) mat_multiply(mat, base);
}
long long fib_matrix(int n)
{
if (n == 0) return 0;
long long mat[2][2] = {{1, 1}, {1, 0}};
mat_power(mat, n - 1);
return mat[0][0];
}
// Method 6: Binet's formula
long long fib_binet(int n)
{
double sqrt5 = sqrt(5.0);
double phi = (1.0 + sqrt5) / 2.0;
double psi = (1.0 - sqrt5) / 2.0;
return (long long)round((pow(phi, n) - pow(psi, n)) / sqrt5);
}
// timing helper
double time_us(clock_t start, clock_t end)
{
return ((double)(end - start)) / CLOCKS_PER_SEC * 1000000.0;
}
int main()
{
int test_cases[] = {0, 1, 10, 20};
int num_cases = sizeof(test_cases) / sizeof(test_cases[0]);
long long memo[100];
printf("=== Fibonacci Comparison ===\n\n");
for (int c = 0; c < num_cases; c++)
{
int n = test_cases[c];
printf("F(%d):\n", n);
clock_t start, end;
start = clock();
printf(" Recursive: %lld", fib_recursive(n));
end = clock();
printf(" (%.0f us)\n", time_us(start, end));
memset(memo, -1, sizeof(memo));
start = clock();
printf(" Memoization: %lld", fib_memo(n, memo));
end = clock();
printf(" (%.0f us)\n", time_us(start, end));
start = clock();
printf(" Iterative: %lld", fib_iterative(n));
end = clock();
printf(" (%.0f us)\n", time_us(start, end));
start = clock();
printf(" Space-optimized: %lld", fib_optimized(n));
end = clock();
printf(" (%.0f us)\n", time_us(start, end));
start = clock();
printf(" Matrix: %lld", fib_matrix(n));
end = clock();
printf(" (%.0f us)\n", time_us(start, end));
start = clock();
printf(" Binet: %lld", fib_binet(n));
end = clock();
printf(" (%.0f us)\n", time_us(start, end));
printf("\n");
}
return 0;
}
运行该程序将输出
=== Fibonacci Comparison ===
F(0):
Recursive: 0 (0 us)
Memoization: 0 (0 us)
Iterative: 0 (0 us)
Space-optimized: 0 (0 us)
Matrix: 0 (0 us)
Binet: 0 (0 us)
F(1):
Recursive: 1 (0 us)
Memoization: 1 (0 us)
Iterative: 1 (0 us)
Space-optimized: 1 (0 us)
Matrix: 1 (0 us)
Binet: 1 (0 us)
F(10):
Recursive: 55 (5 us)
Memoization: 55 (2 us)
Iterative: 55 (1 us)
Space-optimized: 55 (0 us)
Matrix: 55 (3 us)
Binet: 55 (1 us)
F(20):
Recursive: 6765 (520 us)
Memoization: 6765 (3 us)
Iterative: 6765 (1 us)
Space-optimized: 6765 (0 us)
Matrix: 6765 (4 us)
Binet: 6765 (1 us)
C 语言版本使用 clock() 进行计时,编译时需添加 -lm 链接数学库。
Python 实现
import math
import time
# Method 1: Simple Recursion
def fib_recursive(n):
if n == 0: return 0
if n == 1: return 1
return fib_recursive(n - 1) + fib_recursive(n - 2)
# Method 2: Memoization
def fib_memo(n, memo=None):
if memo is None:
memo = {}
if n == 0: return 0
if n == 1: return 1
if n in memo: return memo[n]
memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo)
return memo[n]
# Method 3: Iterative with array
def fib_iterative(n):
if n == 0: return 0
if n == 1: return 1
dp = [0] * (n + 1)
dp[0] = 0
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
# Method 4: Space-optimized iterative
def fib_optimized(n):
if n == 0: return 0
if n == 1: return 1
prev2, prev1 = 0, 1
for i in range(2, n + 1):
prev2, prev1 = prev1, prev1 + prev2
return prev1
# Method 5: Matrix exponentiation
def mat_multiply(a, b):
return [
[a[0][0]*b[0][0] + a[0][1]*b[1][0], a[0][0]*b[0][1] + a[0][1]*b[1][1]],
[a[1][0]*b[0][0] + a[1][1]*b[1][0], a[1][0]*b[0][1] + a[1][1]*b[1][1]]
]
def mat_power(mat, n):
if n == 0 or n == 1:
return mat
base = [[1, 1], [1, 0]]
mat = mat_power(mat, n // 2)
mat = mat_multiply(mat, mat)
if n % 2 != 0:
mat = mat_multiply(mat, base)
return mat
def fib_matrix(n):
if n == 0: return 0
result = mat_power([[1, 1], [1, 0]], n - 1)
return result[0][0]
# Method 6: Binet's formula
def fib_binet(n):
sqrt5 = math.sqrt(5)
phi = (1 + sqrt5) / 2
psi = (1 - sqrt5) / 2
return round((phi ** n - psi ** n) / sqrt5)
def time_us(func, *args):
start = time.perf_counter()
result = func(*args)
end = time.perf_counter()
return result, (end - start) * 1_000_000
test_cases = [0, 1, 10, 20]
print("=== Fibonacci Comparison ===\n")
for n in test_cases:
print(f"F({n}):")
result, t = time_us(fib_recursive, n)
print(f" Recursive: {result} ({t:.0f} us)")
result, t = time_us(fib_memo, n)
print(f" Memoization: {result} ({t:.0f} us)")
result, t = time_us(fib_iterative, n)
print(f" Iterative: {result} ({t:.0f} us)")
result, t = time_us(fib_optimized, n)
print(f" Space-optimized: {result} ({t:.0f} us)")
result, t = time_us(fib_matrix, n)
print(f" Matrix: {result} ({t:.0f} us)")
result, t = time_us(fib_binet, n)
print(f" Binet: {result} ({t:.0f} us)")
print()
运行该程序将输出
=== Fibonacci Comparison ===
F(0):
Recursive: 0 (0 us)
Memoization: 0 (0 us)
Iterative: 0 (0 us)
Space-optimized: 0 (0 us)
Matrix: 0 (0 us)
Binet: 0 (0 us)
F(1):
Recursive: 1 (0 us)
Memoization: 1 (0 us)
Iterative: 1 (0 us)
Space-optimized: 1 (0 us)
Matrix: 1 (1 us)
Binet: 1 (0 us)
F(10):
Recursive: 55 (12 us)
Memoization: 55 (7 us)
Iterative: 55 (3 us)
Space-optimized: 55 (1 us)
Matrix: 55 (8 us)
Binet: 55 (2 us)
F(20):
Recursive: 6765 (1100 us)
Memoization: 6765 (9 us)
Iterative: 6765 (3 us)
Space-optimized: 6765 (2 us)
Matrix: 6765 (10 us)
Binet: 6765 (2 us)
Python 版本使用 time.perf_counter() 进行高精度计时。由于 Python 解释器的开销,各方法的绝对耗时比 C/C++ 高,但相对趋势一致。
Go 实现
package main
import (
"fmt"
"math"
"time"
)
// Method 1: Simple Recursion
func fibRecursive(n int) int64 {
if n == 0 {
return 0
}
if n == 1 {
return 1
}
return fibRecursive(n-1) + fibRecursive(n-2)
}
// Method 2: Memoization
func fibMemo(n int, memo map[int]int64) int64 {
if n == 0 {
return 0
}
if n == 1 {
return 1
}
if val, ok := memo[n]; ok {
return val
}
memo[n] = fibMemo(n-1, memo) + fibMemo(n-2, memo)
return memo[n]
}
// Method 3: Iterative with array
func fibIterative(n int) int64 {
if n == 0 {
return 0
}
if n == 1 {
return 1
}
dp := make([]int64, n+1)
dp[0] = 0
dp[1] = 1
for i := 2; i <= n; i++ {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
// Method 4: Space-optimized iterative
func fibOptimized(n int) int64 {
if n == 0 {
return 0
}
if n == 1 {
return 1
}
var prev2 int64 = 0
var prev1 int64 = 1
for i := 2; i <= n; i++ {
prev2, prev1 = prev1, prev1+prev2
}
return prev1
}
// Method 5: Matrix exponentiation
type Matrix [2][2]int64
func matMultiply(a, b Matrix) Matrix {
return Matrix{
{a[0][0]*b[0][0] + a[0][1]*b[1][0], a[0][0]*b[0][1] + a[0][1]*b[1][1]},
{a[1][0]*b[0][0] + a[1][1]*b[1][0], a[1][0]*b[0][1] + a[1][1]*b[1][1]},
}
}
func matPower(mat Matrix, n int) Matrix {
if n == 0 || n == 1 {
return mat
}
base := Matrix{{1, 1}, {1, 0}}
mat = matPower(mat, n/2)
mat = matMultiply(mat, mat)
if n%2 != 0 {
mat = matMultiply(mat, base)
}
return mat
}
func fibMatrix(n int) int64 {
if n == 0 {
return 0
}
result := matPower(Matrix{{1, 1}, {1, 0}}, n-1)
return result[0][0]
}
// Method 6: Binet's formula
func fibBinet(n int) int64 {
sqrt5 := math.Sqrt(5)
phi := (1 + sqrt5) / 2
psi := (1 - sqrt5) / 2
return int64(math.Round((math.Pow(phi, float64(n)) - math.Pow(psi, float64(n))) / sqrt5))
}
func timeExec(fn func() int64) (int64, time.Duration) {
start := time.Now()
result := fn()
return result, time.Since(start)
}
func main() {
testCases := []int{0, 1, 10, 20}
fmt.Println("=== Fibonacci Comparison ===")
fmt.Println()
for _, n := range testCases {
fmt.Printf("F(%d):\n", n)
result, t := timeExec(func() int64 { return fibRecursive(n) })
fmt.Printf(" Recursive: %d (%d us)\n", result, t.Microseconds())
memo := make(map[int]int64)
result, t = timeExec(func() int64 { return fibMemo(n, memo) })
fmt.Printf(" Memoization: %d (%d us)\n", result, t.Microseconds())
result, t = timeExec(func() int64 { return fibIterative(n) })
fmt.Printf(" Iterative: %d (%d us)\n", result, t.Microseconds())
result, t = timeExec(func() int64 { return fibOptimized(n) })
fmt.Printf(" Space-optimized: %d (%d us)\n", result, t.Microseconds())
result, t = timeExec(func() int64 { return fibMatrix(n) })
fmt.Printf(" Matrix: %d (%d us)\n", result, t.Microseconds())
result, t = timeExec(func() int64 { return fibBinet(n) })
fmt.Printf(" Binet: %d (%d us)\n", result, t.Microseconds())
fmt.Println()
}
}
运行该程序将输出
=== Fibonacci Comparison ===
F(0):
Recursive: 0 (0 us)
Memoization: 0 (0 us)
Iterative: 0 (0 us)
Space-optimized: 0 (0 us)
Matrix: 0 (0 us)
Binet: 0 (0 us)
F(1):
Recursive: 1 (0 us)
Memoization: 1 (0 us)
Iterative: 1 (0 us)
Space-optimized: 1 (0 us)
Matrix: 1 (0 us)
Binet: 1 (0 us)
F(10):
Recursive: 55 (3 us)
Memoization: 55 (2 us)
Iterative: 55 (1 us)
Space-optimized: 55 (0 us)
Matrix: 55 (2 us)
Binet: 55 (1 us)
F(20):
Recursive: 6765 (390 us)
Memoization: 6765 (3 us)
Iterative: 6765 (1 us)
Space-optimized: 6765 (0 us)
Matrix: 6765 (3 us)
Binet: 6765 (1 us)
Go 语言版本使用 time.Now() 和 time.Since() 进行计时,time.Duration.Microseconds() 转换为微秒。Go 的执行效率接近 C/C++。
斐波那契数的性质
各方法对比
| 方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 递归法 | O(2^n) | O(n) | 直观易懂,直接翻译数学定义 | 指数级时间,n > 40 后极慢 |
| 记忆化递归 | O(n) | O(n) | 避免重复计算,思路清晰 | 需要额外存储空间,递归栈开销 |
| 迭代法 | O(n) | O(n) | 简单高效,无递归栈溢出风险 | 需要额外数组存储 |
| 空间优化迭代法 | O(n) | O(1) | 最实用的方法,空间效率最优 | 无法保存中间结果 |
| 矩阵快速幂法 | O(log n) | O(log n) 或 O(1) | 对极大 n 最优 | 实现复杂,小规模无优势 |
| 通项公式法 | O(1) | O(1) | 常数时间,理论上最快 | 浮点精度限制,n > 70 误差大 |
实际应用
- 黄金比例(Golden Ratio):F(n+1)/F(n) 随着 n 增大趋近于 φ = (1+√5)/2 ≈ 1.618。这一比值广泛出现在艺术、建筑和自然界中。
- 动态规划(Dynamic Programming)入门:斐波那契数列是学习 DP 的经典案例,展示了从暴力递归到记忆化再到迭代的优化过程。
- 自然界中的斐波那契:向日葵的种子螺旋数、松果鳞片的排列、鹦鹉螺的壳体曲线等,都遵循斐波那契数列规律。
- 算法与数据结构:斐波那契搜索、斐波那契堆等高级数据结构以斐波那契数列为基础。
整数溢出注意事项
在 C/C++ 中,long long 类型为 64 位,最大可表示的斐波那契数为 F(92) = 7540113804746346429。计算 F(93) 及以上时会溢出。各语言的处理方式:
| 语言 | 溢出处理 |
|---|---|
| C/C++ | long long 可到 F(92);更大值需使用大整数库如 GMP |
| Go | int64 可到 F(92);可使用 math/big 包处理大数 |
| Python | 原生支持任意精度整数,无溢出问题 |
示例:各关键点的斐波那契数值
F(0) = 0
F(1) = 1
F(10) = 55
F(20) = 6765
F(30) = 832040
F(50) = 12586269025
F(92) = 7540113804746346429 (long long 极限)
F(100) = 354224848179261915075 (需要大整数支持)

浙公网安备 33010602011771号