标题: 黄金连分数
黄金分割数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;
}