蓝桥杯 黄金连分数 斐波那契数列 C++

黄金分割数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位数字,请通过浏览器直接提交该数字。

思路

  • 列出不同精度的黄金连分数: 1/1, 1/2, 2/3, 3/5, 5/8, 8/13… 就可以发现分子构成了一个斐波那契数列

坑点

  • 必须计算到数稳定, 比如只算到小数点后200位, 和只算到小数点后150位, 他们的小数点后前100位其实是不同的, 不信看下面
    200: 0.61803398874989484820458683436563811772030917980576286213544862270526046281890244970689060991186760494835495715193107432580066811482537118342659423316518035141312446804875934396442616297830347146545065582
    150:
    0.618033988749894848204586834365638117720309179805762862135448622454495367529649390807995503107673208287054107545247921863679942111302171933471925734393957
    必须算到足够大, 数才稳定, 取它的101位四舍五入到100位才是正确的答案
  • 要算到足够大的数, 就必须自己写大数加减除法了
#include <iostream>
#include <string>
#include <algorithm> 

using namespace std;

int n = 500;

// 这里提供了两种add方法
// 这个是针对斐波那契数列数列的, 即a<b
string add2(string a, string b){
	int i=a.length()-1; // a字符串的当前计算位置
	int j=b.length()-1; // b字符串的当前计算位置 
	int flag=0; // 进位标志
	for(; i>=0; i--, j--){  // 从个位开始
		char cur = a[i]+b[j]-'0'+flag;  // 两个数的ASCII相加, 再加上进位
		flag=0;
		if(cur>'9'){  
			flag=1;  // 如果超过'9', 进位
			cur -= 10;
		}
		b[j]=cur;
	}
	if(flag){
		if(j<0){  // 有进位, 且j需要加一位
			b.insert(0, "0");  // 先把新添的一位置为0, 下面一起加
			j=0;
		}
		b[j] += flag;
	}
	return b;
}

// 比较通用的加法
string add(string a, string b){
	a = a.substr(a.find_first_not_of('0'));  // 取出字符串前面的0
	b = b.substr(b.find_first_not_of('0'));
	long long lenA = a.length();
	long long lenB = b.length();
	long long len = max(a.length(), b.length()) + 10; // 总长
	
	reverse(a.begin(), a.end());  // 翻转便于操作
	reverse(b.begin(), b.end());
	
	string ans(len, '0');  // initialize ans with 0
	for(int i=0; i<lenA; i++){
		ans[i] = a[i];
	}
	int tmp=0;  // add flag
	int i;
	for(i=0; i<len, i<lenB; i++){
		tmp += ((ans[i]-'0') + (b[i]-'0'));
		ans[i] = tmp%10+'0';
		tmp /= 10;
	}
	ans[i] += tmp;
	reverse(ans.begin(), ans.end());
	return ans.substr(ans.find_first_not_of('0'));
}

int cmp(string a, string b){
//  相等返回0, a>b返回1, 其他返回-1 
	if(a.find_first_not_of('0')==string::npos) a="0";  // 如果是0, 00, 000 都要置为0 
	else a.substr(a.find_first_not_of('0'));
	if(b.find_first_not_of('0')==string::npos) b="0";
	else b.substr(b.find_first_not_of('0'));
	
	if(a==b) return 0;
	if(a.length() == b.length()){
		if(a>b) return 1;
		else return -1;
	}
	if(a.length() > b.length()) return 1;
	else return -1;
}

string subtract(string a, string b){
//	只考虑a>=b, 偷个懒
	long lenA = a.length();
	long lenB = b.length();

	reverse(a.begin(), a.end());
	reverse(b.begin(), b.end());
	int i;
	int flag = 0;  // 借位标志
	for(i=0; i<lenB; i++){
		a[i] -= flag;  // 减去借位标志
		flag = 0;
		if(a[i]<b[i]){
			a[i] += 10;  // 借位
			flag = 1;
		}
		a[i] = a[i]-b[i]+'0';  // 一位减法
	}
    // b减完了, 处理剩下的借位
	while(flag){
		a[i] -= flag;
		flag = 0;
		if(a[i]<'0'){
			a[i] += 10;
			flag = 1;
			i++;
		}
	}
	reverse(a.begin(), a.end());
	if(a.find_first_not_of('0') == string::npos) return "0";  // !!
	return a.substr(a.find_first_not_of('0'));
}

void i2s(int n, string &s){
// 整型到字符型
	if(n==0){
		s = "0";
		return;
	}
	
	s = "";
	while(n){
		s.append(1, n%10 + '0');
		n/=10;
	}
	reverse(s.begin(), s.end());
}

string divide(string a, string b){
//	assume a<b
	string ans = "0.";
	for(int i=0; i<n; i++){
		a.append("0");
		int t = 0;
		while(cmp(a, b)>=0){  // a>=b, 
			a = subtract(a, b);  // 不断做减法, 直到不能减
			t++;  // 记录减了多少个了
		}
		string t_str;
		i2s(t, t_str);
		ans.append(t_str);
	}
	return ans;
}

int main(int argc, char** argv){
	string a = "1";
	string b = "1";
	for(int i=3; i<=n; i++){
		string _ = b;
		b = add2(a, b);
		a = _;
//		cout<<b<<endl; 
	}
	string ans = divide(a, b);
	cout<<ans<<endl;
	cout<<ans.length()-2<<endl;
} 

答案就是:
0.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值