这是我需要编程的数学计算 - 将序列a n 定义为
如果 2014 = p / q,请找到p + q
而且,这是我尝试过的代码:
#include <stdio.h>
#include <math.h>
main() {
int n=1;
double an=1.0;
double an_floor=0.0;
while (n<=2014) {
an_floor = floor(an);
an = 1 / (2*an_floor-an+1);
n = n + 1;
}
printf("%lf", an);
return 0;
}
问题;
我无法编译(使用网络编译器,如键盘等)
不知道如何制作结果
分数结果错误
答案 0 :(得分:1)
对于那些总是喜欢提醒人们浮点错误的人来说,在这种情况下我被抓住了。
如果您运行以下程序,
/* a1 = 1, a(n+1) = 1/(2*[an]-an+1) ([x] is floor function)
* a2014 = p/q
* find p+q
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define LAST_ELEMENT 2014
static double
next_element(double prev) {
return 1.0 / (2.0 * floor(prev) - prev + 1.0);
}
int main(void) {
int i = 0;
double last = 1.0;
for (i = 0; i < LAST_ELEMENT; i += 1) {
last = next_element( last );
}
printf("%g\n", last);
return EXIT_SUCCESS;
}
你得到这个输出:
C:\...\Temp> cl /fp:precise /O2 rrr.c C:\...\Temp> rrr.exe 2.25
但是,这是由于浮点错误为@JJoao points out。他概述了一种处理这一特定问题的具体方法。
另一种方法是利用任意精度数学库来帮助你。首先,让我们使用快速Perl脚本验证问题:
#!/usr/bin/env perl
use strict;
use warnings;
use POSIX qw( floor );
sub next_element { '1' / (('2' * floor($_[0])) - $_[0] + '1.0') }
sub main {
my ($x, $n) = @_;
$x = next_element( $x ) for 1 .. $n;
printf("%g\n", $x);
}
main(1, 2014);
输出:
C:\...\Temp> perl t.pl 2.25
与C程序相同。
现在,使用Perl的bignum:
C:\...\Temp> perl -Mbignum t.pl 5.83333
这种提高的准确性来自性能成本。如果没有bignum
,脚本将在0.125秒内运行,其余的事情大约为0.094,而不是计算。使用bignum
,大约需要两秒钟。
现在,现代C提供了各种设施来操纵浮动数字的舍入方式。在这种特殊情况下,考虑到楼层功能的性质,将舍入模式设置为FE_DOWNWARD
将解决问题:
#include <fenv.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define LAST_ELEMENT 2014
static double
next_element(double prev) {
return 1.0 / (2.0 * floor(prev) - prev + 1.0);
}
int main(void) {
int i = 0;
double last = 1.0;
const int original_rounding = fegetround();
fesetround(FE_DOWNWARD);
for (i = 0; i < LAST_ELEMENT; i += 1) {
last = next_element(last);
}
fesetround(original_rounding);
printf("%g\n", last);
return EXIT_SUCCESS;
}
输出:
C:\...\Temp> cl /O2 /fp:precise rrr.c /out:rrr.exe C:\...\Temp> rrr 5.83333
答案 1 :(得分:1)
floor(潜在表示错误的值)=大错误!!
floor(3-epsilon)= 2
floor(3+epsilon)= 3
大量累计实数计算=大错误!!
使用我们得到的OP代码
1
0.5
2
0.333333
1.5
0.666667
3
0.5 <===== Big precision ERROR it should be 0.25
2
1
2.2518e+15A <===== infinity
4.44089e-16 <===== zero
1
0.5
....(wrong) cyclic behavior
final(错!!)结果= 2.2518e+15A <===== infinity
1)floor
问题:用floor(x)
替换floor(x + 2*expectedError)
2)关于累积错误:将double
表示替换为(v1:int, v2:int)
分数表示。An -> v1/v2
newAn = newv1/newv2 =
= 1 / (2 * flo(An) + 1 - An) =
= 1 / (2 * flo(v1/v2) + 1 - v1/v2) =
= ... =
= v2 / ( 2 * v2 * flo(v1/v2) + v2 - v1)
这样
newv1 = v2
newv2 = 2 * v2 * flo(v1/v2) + v2 - v1
在C:
main() {
int v1=1, v2=1, n=1, oldv1;
double an_floor;
while (n<=2014) {
an_floor = floor((0.0+v1)/v2 + 0.0000000001);
oldv1=v1;
v1=v2;
v2=an_floor * v2 * 2 + v2 - oldv1;
n++;
// printf("%g\n", (0.0+v1)/v2);
}
printf("%d/%d=%g\n", v1, v2, (0.0+v1)/v2); // 35/6=5.83333
}
\ thanks {SinanÜnür}