本系列算法实现都是在学习数据结构(C++语言版),清华大学邓俊辉教授编,听邓老师edX网课过程中自己实现的例子。
问题: 求Fibonacci第n项?
解决: 用以下四种种方法实现。
- 递归
- 公式法
- 动态规划
- 矩阵
参考链接如下:
1 递归
递归最简单,但是算法复杂度高,为O(2^n)
//! usual recursion
// 0 1 2 3 5 8
int fib_recursion(int n)
{
return n < 2 ? n : fib_recursion(n - 1) + fib_recursion(n - 2);
}
2 公式法
//! calculate by formular
int fib_formular(int n)
{
const double constant_a = (1 + sqrt(5)) / 2;
const double constant_b = (1 - sqrt(5)) / 2;
const double constant_c = sqrt(5) / 5;
return constant_c * (fast_pow_iterate(constant_a, n) - fast_pow_iterate(constant_b, n));
}
3 动态规划
有两种实现方法,基本思想是DP,动态规划包含内容很多,可以专门学习下。
//! dynamic programming (DP)
int fib_dynamic_programming_n(int n)
{
std::vector<int> a(n + 1);
for (int i = 0; i < n + 1; ++i)
{
i < 2 ? a[i] = i : a[i] = a[i - 1] + a[i - 2];
}
return a[n];
}
//! dynamic programming (DP)
int fib_dynamic_programming_3(int n)
{
int cur = 1, pre = 0;
//base
if (n < 2)
return n;
//iterator n-1
while (--n)
{
cur = cur + pre;
pre = cur - pre;
}
return cur;
}
4 矩阵
递推公式如下,
公式推导见fibonacci 维基百科和Gocalf blog——计算斐波纳契数,分析算法复杂度
#include <eigen3/Eigen/Eigen>
//! matrix method
int fib_matrix(int n)
{
Eigen::Matrix2i r;
Eigen::Matrix2i base;
base << 1, 1, 1, 0;
fast_pow_matrix(base, n, r);
return r(0, 1);
}
其中关于快速求解矩阵幂使用的是以下算法
#include <eigen3/Eigen/Eigen>
//! solve matrix^n
void fast_pow_matrix(Eigen::Matrix2i& base, int n, Eigen::Matrix2i& r)
{
r << 1, 0, 0, 1;
while (n > 0)
{
if (n & 1)
r *= base;
base *= base;
n >>= 1;//n=n/2
}
}
其实用到的算法和求单个数字的快速幂是一样的,算法如下,有两种方法,分别是迭代和递归。
//! solve base^n
template <typename T>
T fast_pow_iterate(T base, int n)
{
T r = 1;
while (n > 0)
{
if (n & 1)
r *= base;
base *= base;
n >>= 1;//n=n/2
}
return r;
}
//! solve base^n
template <typename T>
T fast_pow_recursion(T base, int n)
{
//recursion base
if (n == 1)
return base;
T tmp = fast_pow_recursion(base, n >> 1);
return (n % 2 ? base : 1) * tmp * tmp;
}
5 根据动态规划实现的一个Fibonacci类
#pragma once
class Fibonacci
{
public:
Fibonacci(int n_): fibn(n_)
{
cur = 1, pre = 0;
//iterator n-1
while (--n_)
{
cur = cur + pre;
pre = cur - pre;
}
}
~Fibonacci(){};
void reset_fib(int n_)
{
while (n_ != fibn)
{
n_<fibn?get_pre():get_next();
}
}
int get_fibn()
{
return fibn;
}
int get_cur()
{
return cur;
}
int get_pre()
{
fibn--;
pre = cur - pre;
cur = cur - pre;
return cur;
}
int get_next()
{
fibn++;
cur = cur + pre;
pre = cur - pre;
return cur;
}
private:
int fibn; //record n
int cur;
int pre;
};