黄金分割数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