趣学算法--斐波那契数列

本文深入探讨了斐波那契数列的起源,从兔子繁殖问题出发,介绍了递归、优化递归及O(logN)时间复杂度的矩阵快速幂算法。此外,还阐述了斐波那契数列在植物叶序、黄金比例以及矩形面积、杨辉三角和跳台阶问题中的应用。通过对不同算法的实现与效率分析,展示了数学与编程的美妙结合。
摘要由CSDN通过智能技术生成

14天阅读挑战赛
努力是为了不平庸~

 

目录

1.神奇的兔子数列

        1.1问题分析

        1.2斐波那契数列

        1.3算法设计

                1.3.1递归代码

                1.3.2递归的改进,O(n)的代码

                1.3.3时间复杂度为O(logN)的代码

                1.3.4 O(logN)算法的思路

                1.3.5 main函数及测试结果

2.生活中的斐波那契数列

3.黄金比例

4.斐波那契数列的一些应用

        4.1矩形面积

        4.2杨辉三角

        4.3跳台阶问题


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阶台阶有多少种走法?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值