14天阅读挑战赛
努力是为了不平庸~
目录
1.神奇的兔子数列
假设第1个月有1对初生的兔子,第2个月进入成熟期,第3个月开始生育兔子,而1对成熟的兔子每月会生1对兔子,兔子永不死去......那么,由1对初生的兔子开始,12个月后会有多少对兔子呢?
1.1问题分析
不妨拿新出生的1对小兔子分析。
第1个月,小兔子1没有繁殖能力,所以还是1对。
第2个月,小兔子1进入成熟期,仍然是1对。
第3个月,小兔子1生了一对小兔子2,于是这个月共有2对兔子。
第4个月,小兔子1又生了小兔子3,因此共有3対兔子。
第5个月,小兔子1生了一对小兔子4,而在第3个月出生的小兔子2也生下一对小兔子5,因此共有5(2+3=5)对兔子。
第6个月,兔子123各生下1对小兔子,新生的3对兔子加上原有的5对兔子,这个月共有8(3+5=8)对兔子。
......
这个数列有如下十分明显的特点:从第3个月开始,当月的兔子数=上月兔子数+当月新生兔子数,而当月新生兔子数正好等于上上月的兔子数。因此,前面相邻两项之和便构成一项,换言之:
当月兔子数 = 上月兔子数 + 上上月的兔子数
1.2斐波那契数列
斐波那契数列如下:
1,1,2,3,5,8,13,21,34,55,......
递归表达式如下:
1.3算法设计
1.3.1递归代码
//算法1:递归 O(n²)
unsigned long Func1(int n)
{
if (n == 1 || n == 2)
{
return 1;
}
return Func1(n - 1) + Func1(n - 2);
}
1.3.2递归的改进,O(n)的代码
//算法2:用中间值保存变量O(n)
double Func2(int n)
{
double fir = 1;
double sec = 1;
if (n == 1 || n == 2)
{
return 1;
}
cout << "数列:1 1";
double result = 0;
for (int i = 3; i <=n; i++)
{
result = fir + sec;
fir = sec;
sec = result;
printf(" %.0f", result);
}
cout << endl << endl;
return result;
}
1.3.3时间复杂度为O(logN)的代码
//算法3:O(logn)
struct Matrix
{
double m11;
double m12;
double m21;
double m22;
};
//矩阵相乘
Matrix MatrixMul(const Matrix& lhs, const Matrix& rhs)
{
Matrix result;
result.m11 = lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21;
result.m12 = lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22;
result.m21 = lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21;
result.m22 = lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22;
return result;
}
Matrix Fib(Matrix base, int num)
{
if (num == 1)
{
return base;
}
Matrix orgMatrix = Fib(base, num / 2);
Matrix result = MatrixMul(orgMatrix, orgMatrix);
//如果num是奇数,需要再做一次与初矩阵的相乘
if (num % 2)
{
result = MatrixMul(result, base);
}
return result;
}
double Func3(int n)
{
Matrix base = { 1,1,1,0 };
if (n == 1 || n == 2)
{
return 1;
}
Matrix result = Fib(base, n);
printf("\n算法3运行结果:%.0f\n", result.m11 + result.m21);
return result.m11 + result.m21;
}
1.3.4 O(logN)算法的思路
1.3.5 main函数及测试结果
//Fibonaqi
#include <windows.h>
#include <time.h>
namespace Fibonaqi
{
//算法1:递归 O(n²)
unsigned long Func1(int n)
{
if (n == 1 || n == 2)
{
return 1;
}
return Func1(n - 1) + Func1(n - 2);
}
//算法2:用中间值保存变量O(n)
double Func2(int n)
{
double fir = 1;
double sec = 1;
if (n == 1 || n == 2)
{
return 1;
}
cout << "数列:1 1";
double result = 0;
for (int i = 3; i <=n; i++)
{
result = fir + sec;
fir = sec;
sec = result;
printf(" %.0f", result);
}
cout << endl << endl;
return result;
}
//算法3:O(logn)
struct Matrix
{
double m11;
double m12;
double m21;
double m22;
};
//矩阵相乘
Matrix MatrixMul(const Matrix& lhs, const Matrix& rhs)
{
Matrix result;
result.m11 = lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21;
result.m12 = lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22;
result.m21 = lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21;
result.m22 = lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22;
return result;
}
Matrix Fib(Matrix base, int num)
{
if (num == 1)
{
return base;
}
Matrix orgMatrix = Fib(base, num / 2); //通过二分法的方式,实现时间复杂度为O(logn)的效果
Matrix result = MatrixMul(orgMatrix, orgMatrix);
if (num % 2)
{
result = MatrixMul(result, base);
}
return result;
}
double Func3(int n)
{
Matrix base = { 1,1,1,0 };
if (n == 1 || n == 2)
{
return 1;
}
Matrix result = Fib(base, n);
printf("\n算法3运行结果:%.0f\n", result.m11 + result.m21);
return result.m11 + result.m21;
}
void Run()
{
cout << "输入第n个数:";
int num = 0;
cin >> num;
cout << endl;
//开始运行时间 -- 算法1
long startTime = clock();
int fib_result = Fibonaqi::Func1(num);
//结束运行时间
long endTime = clock();
cout << "算法1运行时间:" << endTime - startTime << endl;
cout << "结果:" << fib_result << endl << endl;
//开始运行时间 -- 算法2
startTime = clock();
double result = Fibonaqi::Func2(num);
//结束运行时间
endTime = clock();
cout << "算法2运行时间:" << endTime - startTime << endl;
printf("算法2运行结果:%.0f\n", result);
//开始运行时间 -- 算法3
startTime = clock();
result = Fibonaqi::Func3(num - 2);//注意这里输入的数减了2
//结束运行时间
endTime = clock();
cout << "算法3运行时间:" << endTime - startTime << endl;
}
}
int main()
{
Fibonaqi::Run();
system("pause");
return 0;
}
2.生活中的斐波那契数列
科学家经研究,在植物的叶、枝、茎等排列中发现了斐波那契数!例如,在树木的枝干上选一片叶子,记数为1,然后依序点数叶子(假定没有折损),直至数到与那片叶子正对的位置,则期间的叶子数多半是斐波那契数列。叶子从一个位置到达下一个正对的位置称为一个循回。叶子在一个循回中旋转的圈数也是斐波那契数。在一个循回中,叶子数与叶子旋转圈数的比称为叶序(源自希腊词,意为叶子的排列)比。多数植物的叶序比呈现为斐波那契数的比。例如,蓟的头部具有34条顺时针旋转和21条逆时针的斐波那契螺旋,向日葵的种子的圈数与种子数、菠萝的外部排列等同样具有这样的特性,如下图:
观察延龄草、野玫瑰、南美血根草、大波斯菊、金凤花、耧斗菜、百合花、蝴蝶花的花瓣,可以发现它们的花瓣为斐波那契数:3,5,8,13,21,......,如下图:
3.黄金比例
有趣的是,这样一个完全由自然数组成的数列,通项公式却是用无理数来表达的,而且当n趋向于无穷大时,斐波那契数列中的前一项与后一项的比值越来越逼近黄金分割数0.618: 1/1=1, 1/2=0.5, 2/3=0.666,......,3/5=0.6, 5/8=0.625,......,55/89=0.617977, 144/233=0.618025,......,46368/75025=0.6180339886......
越到后面,这些比值越接近黄金分割数:
4.斐波那契数列的一些应用
4.1矩形面积
斐波那契数列与矩形面积的生成相关,由此可以导出一个斐波那契数列的一个性质。斐波那契数列前几项的平方和可以看做不同大小的正方形,由于斐波那契的递推公式,它们可以拼成一个大的矩形。这样所有小正方形的面积之和等于大矩形的面积。则可以得到如下的恒等式:
4.2杨辉三角
将杨辉三角左对齐,成如图所示排列,将同一斜行的数加起来,即得一数列1、1、2、3、5、8、……
4.3跳台阶问题
有个n阶台阶,一次可以走一个台阶,也可以走两个台阶,走到n阶台阶有多少种走法?