黄金连分数(斐波那契数列、大数运算)正确解法


标题: 黄金连分数
    黄金分割数0.61803... 是个无理数,这个常数十分重要,在许多工程问题中会出现。有时需要把这个数字求得很精确。

    对于某些精密工程,常数的精度很重要。也许你听说过哈勃太空望远镜,它首次升空后就发现了一处人工加工错误,对那样一      个庞然大物,其实只是镜面加工时有比头发丝还细许多倍的一处错误而已,却使它成了“近视眼”!!
    言归正传,我们如何求得黄金分割数的尽可能精确的值呢?有许多方法。

    比较简单的一种是用连分数:

                  1
    黄金数 = ---------------------
                        1
             1 + -----------------
                          1
                 1 + -------------
                            1
                     1 + ---------
                          1 + ...                    

    这个连分数计算的“层数”越多,它的值越接近黄金分割数。

    请你利用这一特性,求出黄金分割数的足够精确值,要求四舍五入到小数点后100位。

    小数点后3位的值为:0.618
    小数点后4位的值为:0.6180
    小数点后5位的值为:0.61803
    小数点后7位的值为:0.6180340
   (注意尾部的0,不能忽略)

你的任务是:写出精确到小数点后100位精度的黄金分割值。

注意:尾数的四舍五入! 尾数是0也要保留!

显然答案是一个小数,其小数点后有100位数字,请通过浏览器直接提交该数字。
注意:不要提交解答过程,或其它辅助说明类的内容。

解题思路:

此题在网上看过若干解法,大同小异,但基本都是错误的。

下面是一种正确解法。

首先,题目所说的黄金连分数实际上就是求解某一项斐波那契数列n/n+1项的比值,要精确到小数点100位。

此题网上解法错误在于大多使用了较小项的斐波那契数,一般在40到70项,实际上,100位精确小数是要做到无误差的,因此必须不断加大斐波那契的项数不断测试,下面我给出一个图,大家就明白了。

实际上这个比值在102项/101项斐波那契数的时候才开始不再变化,即为我们要求的结果。

因此难度在于100多项斐波那契数的时候,我们的整型是无法计算如此大的数据。因此需要通过大数加法以及大数除法实现计算。

下面给出代码:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
//思路:由黄金连分数可转化为求解斐波那契数,运用大数运算求解小数点后一百位

//n表示斐波那契数列第一百项
int n = 100; 
int cmp(string a, string b)
{
	if (a == b) return 0;
	else if (a.length() < b.length()) return -1;
	else if (a.length() > b.length()) return 1;
	else
	{
		if (a > b) return 1;
		if (a < b) return -1;
		return -1;
	}
}

//大数相加,较大斐波那契数无法算出,只能通过大数计算
string add(string a, string b)
{
	//去掉开头的0,substr(a,n) 返回第a位开始长度为n的字符串
	//find_first_not_of("abc")返回最先匹配到abc任意一个字符的最后位置
	a = a.substr(a.find_first_not_of('0'));
	b = b.substr(a.find_first_not_of('0'));

	long long lenA = a.length();
	long long lenB = b.length();
	long long len = max(lenA, lenB) + 10;
	//反转, 便于从最低位逐位求和
	reverse(a.begin(), a.end());
	reverse(b.begin(), b.end());
	string ans(len, '0');
	for (int i = 0; i < lenA; i++)
	{
		ans[i] = a[i];
	}
	//通过模拟手工计算来进行大数相加,temp用来存储上下两位相加的结果
	int temp = 0;
	for (int i = 0; i < len; i++)
	{
		if (i < lenB)
		{
			temp += ((ans[i] - '0') + (b[i] - '0'));
			ans[i] = temp % 10 + '0';
			temp /= 10;
		}
		else
		{
			temp += (ans[i] - '0');
			ans[i] = temp % 10 + '0';
			temp /= 10;
		}
	}
	//ans存储的结果最后要反转回来去掉开头的0
	reverse(ans.begin(), ans.end());
	return ans.substr(ans.find_first_not_of('0'));
}

//大数相减
string substract(string a, string b)
{
	//a必须大于b,实际上在divide()中,a已经>=b了
	reverse(a.begin(), a.end());
	reverse(b.begin(), b.end());
	//将a复制给ans
	string ans = a;
	//依旧是模拟手工减法计算
	for (int i = 0; i < b.length(); i++)
	{
		if (ans[i] >= b[i])
		{
			ans[i] = ans[i] - b[i] + '0';
		}
		//注意:在被减数不够减的情况下,需要向前借位,
		//可能被借位为0也不够借,继续往前寻找不为0的位,通过一个while循环实现
		else
		{
			int k = 1;
			while (ans[i + k] == '0')
			{
				//0被借位变为9
				ans[i + k] = '9';
				k++;
			}
			//最终i+k位不为0可以被借-1,i位+10
			ans[i + k] = ans[i + k] - 1;
			ans[i] = ans[i] + 10 - b[i] + '0';
		}
	}
	reverse(ans.begin(), ans.end());
	return ans.substr(ans.find_first_not_of('0'));
}

//大数相除
string divide(string a, string b)
{
	//前提 a < b 实际题目已满足
	//大数除法实际上是模拟除法运算结合大数减法
	string ans = "0.";
	for (int i = 0; i < 101; i++) //保留101项,保证四舍五入
	{
		// (a*10)/b = t 不过其中除法用减法substract代替
		a.append(1,'0');
		int t = 0;
		while (cmp(a, b) >= 0)
		{
			a = substract(a, b);
			t++;
		}
		ans.append(1,t + '0');
	}
	return ans;
}
int main()
{
	//斐波那契数列前两项
	string a = "1";
	string b = "1";
	for (int k = 0; k < 40; k++)
	{
		//求解斐波那契数列100-140各项与前一项的比值(所谓黄金连分数)
		for (int i = 3; i < n+k; i++)
		{
			string temp = b;
			b = add(a, b);
			a = temp;
		}
		
		string ans = divide(a, b);
		cout << 100+k  << "项  " <<ans << endl;
	}

	return 0;
}

 

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
黄金连分数是指一种特殊的数学序列,也被称为黄金分割数列。它的定义是:从1开始的连分数序列,每一个项都是前一项的倒数加1。具体的公式为: 1 + 1/(1 + 1/(1 + 1/(1 + ...))) 这种序列在数学上有很多有趣的性质和应用。在计算机编程领域,可以用Java编程语言实现黄金连分数序列的计算和求值。 在蓝桥杯中,如果遇到与黄金连分数相关的问题,可以尝试用Java编写程序来解决。首先,我们可以定义一个函数,用于计算黄金连分数的值。函数的参数可以是一个整数n,表示要计算的黄金连分数的项数。函数的返回值是一个浮点数,表示该项的值。 在函数内部,我们可以使用递归的方式来计算黄金连分数。首先,如果n等于1,那么该项的值就是1。否则,我们可以递归调用函数,传入n-1作为参数,得到前一项的值,然后再进行倒数和加1的计算。 接下来,我们可以在主函数中调用该函数,传入一个正整数n作为参数,得到黄金连分数的第n项的值。然后,我们可以将结果打印出来,或者进行其他的操作。例如,可以将黄金连分数的值与某个给定的数进行比较,或者进行其他的数学运算。 总之,黄金连分数是一种特殊的数学序列,它的计算和求值可以用Java编程语言实现。在蓝桥杯中,如果有相关的问题,可以尝试用Java语言来解决。通过编写一个计算黄金连分数的函数,可以方便地得到黄金连分数的任意一项的值,并进行后续的操作和计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值