题目:黄金连分数(12分)
黄金分割数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位数字。
解题思路:手推几层,就容易发现黄金数的连分数形式可以写成斐波那契数列的后一项除以前一项,而且项数却靠后,黄金分割数就越精确。知道了这个,还要解决运算的问题,因为在不断计算斐波那契数列及其除法,加法等等的数据,已经超过所有的数据类型的范围,所以要用高精度的四则运算来解决。最后,如何确定所得答案已经精确到的小数点后100位?因为是两项斐波那契数列相处,所以当 f(i+2)/f(i+1) 与f(i+1)/f(i) 所得的数小数点后100位不再改变,则已经精确了100位,因为要看小数点后100位的数字,所以做除法操作的时候可以将分母扩大10^100,这样就把小数点后面的100位数字提到了整数部分。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1005;
int a[N], b[N], c[N], expansion[N], div1[N], div2[N];
int GetArrayLen(int num[]){ //获得传入数组中存储的数字长度
for(int i=N-1; i>=0; --i){
if(num[i]){
return i+1;
}
}
return 0;
}
void init(){
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
a[0] = 1; b[0] = 1; c[0] = 2;
}
void TransferTo(int num2[], int num1[]){ //将num2中内容转移到num1中
int len2 = GetArrayLen(num2);
for(int i=0; i<len2; ++i){
num1[i] = num2[i];
}
}
void Add(int num1[], int num2[], int num3[]){ //高精度加法,num1+num2=num3
int len1 = GetArrayLen(num1), len2 = GetArrayLen(num2);
int len = max(len1, len2);
for(int i=0; i<len; ++i){
num3[i] += num1[i]+num2[i];
if(num3[i] > 9){
num3[i+1]++;
num3[i] -= 10;
}
}
}
int Subtract(int num1[], int num2[], int left, int right, int len2){
int len1 = right-left+1;
if(len1 < len2){
return -1;
}
if(len1 == len2){
for(int i=right,j=len2-1; i>=left; --i){
if(num1[i] > num2[j]){
break;
}
if(num1[i] < num2[j]){
return -1;
}
--j;
}
}
for(int i=left, j=0; i<=right; ++i){
num1[i] -= num2[j];
if(num1[i] < 0){
num1[i] += 10;
num1[i+1]--;
}
++j;
}
for(int i=right; i>=0; --i){
if(num1[i]){
return i;
}
}
return 0;
}
void Divide(int numerator[], int denominator[], int result[]){ //result = numerator/denominator
memset(expansion, 0, sizeof(expansion));
int len_numerator = GetArrayLen(numerator), len_denominator = GetArrayLen(denominator);
for(int i=0; i<len_numerator; ++i){
expansion[105+i] = numerator[i];
}
int len_expansion = len_numerator+105;
int right = len_expansion-1, tmp;
for(int left = len_expansion-len_denominator; left>=0; --left){
while((tmp = Subtract(expansion, denominator, left, right, len_denominator)) >= 0){
result[left]++;
right = tmp;
}
}
}
bool equals(){ //三项斐波那契数列的除法操作,检查两个得数的前100是否相同
memset(div1, 0, sizeof(div1));
memset(div2, 0, sizeof(div2));
Divide(a, b, div1);
Divide(b, c, div2);
for(int i=0; i<105; ++i){
if(div1[i] != div2[i]){
return false;
}
}
return true;
}
void toShow(){ //打印结果
string result = "0.";
int len = GetArrayLen(div1);
for(int i=len-1; i>=len-101; --i){
result += div1[i]+'0';
}
int lens = result.length();
if(result[lens-1] >= '5'){
result[lens-2] += 1;
}
for(int i=0; i<lens-1; ++i){
cout << result[i];
}
}
void GetGoldNum(){
init();
while(!equals()){
TransferTo(b, a); //数组内容转移,更新三项斐波那契数列
TransferTo(c, b);
memset(c, 0, sizeof(c));
Add(a, b, c);
//while里面为求下一项斐波那契
}
toShow();
}
int main()
{
GetGoldNum();
return 0;
}