我正在寻找获得π价值的最快方法,作为个人挑战。更具体地说,我使用的方法不涉及使用#define
常量,如M_PI
,或在中硬编码数字。
下面的程序测试了我所知道的各种方法。理论上,内联汇编版本是最快的选择,尽管显然不是可移植的。我已经将其作为基准包含进来,以便与其他版本进行比较。在我的测试中,内置了4 * atan(1)
的版本在GCC 4.2上速度最快,因为它会将atan(1)
自动折叠成一个常量。指定-fno-builtin
时,atan2(0, -1)
版本最快。
以下是主要测试程序(pitimes.c
):
# include <math.h>
# include <stdio.h>
# include <time.h>
# define ITERS 10000000
# define TESTWITH(x) {
diff = 0.0;
time1 = clock();
for (i = 0; i < ITERS; ++i)
diff += (x) - M_PI;
time2 = clock();
printf("%st=> %e, time => %fn", #x, diff, diffclock(time2, time1));
}
static inline double
diffclock(clock_t time1, clock_t time0)
{
return (double) (time1 - time0) / CLOCKS_PER_SEC;
}
int
main()
{
int i;
clock_t time1, time2;
double diff;
/* Warmup. The atan2 case catches GCC's atan folding (which would
* optimise the ``4 * atan(1) - M_PI'' to a no-op), if -fno-builtin
* is not used. */
TESTWITH(4 * atan(1))
TESTWITH(4 * atan2(1, 1))
# if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__))
extern double fldpi();
TESTWITH(fldpi())
# endif
/* Actual tests start here. */
TESTWITH(atan2(0, -1))
TESTWITH(acos(-1))
TESTWITH(2 * asin(1))
TESTWITH(4 * atan2(1, 1))
TESTWITH(4 * atan(1))
return 0;
}
以及仅适用于x86和x64系统的内联汇编内容(fldpi.c
):
double
fldpi()
{
double pi;
asm("fldpi" : "=t" (pi));
return pi;
}
以及构建我正在测试的所有配置的构建脚本(build.sh
):
# !/bin/sh
gcc -O3 -Wall -c -m32 -o fldpi-32.o fldpi.c
gcc -O3 -Wall -c -m64 -o fldpi-64.o fldpi.c
gcc -O3 -Wall -ffast-math -m32 -o pitimes1-32 pitimes.c fldpi-32.o
gcc -O3 -Wall -m32 -o pitimes2-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -fno-builtin -m32 -o pitimes3-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -ffast-math -m64 -o pitimes1-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall -m64 -o pitimes2-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall -fno-builtin -m64 -o pitimes3-64 pitimes.c fldpi-64.o -lm
除了在不同的编译器标志之间测试(我也比较了32位和64位,因为优化是不同的),我还尝试了改变测试的顺序。但尽管如此,atan2(0, -1)
版本仍然每次都位居榜首。
15条答案
按热度按时间h4cxqtbf1#
如上所述,Monte Carlo method应用了一些伟大的概念,但显然,它不是最快的,也不是遥不可及的,也不是任何合理的衡量标准。此外,这完全取决于你想要的是什么样的准确性。我所知道的最快的π是硬编码的数字。看看Pi和Pi[PDF],有很多公式。
这里有一种快速收敛的方法--每次迭代大约14位数字。目前速度最快的应用程序PiFast在FFT中使用了此公式。我只写公式,因为代码很简单。这个公式在Ramanujan and discovered by Chudnovsky几乎找到了。这实际上是他计算数十亿位数字的方法--所以这是一个不容忽视的方法。公式将很快溢出,由于我们正在划分阶乘,因此推迟计算以删除项将是有利的。
哪里,
下面是Brent–Salamin algorithm。维基百科提到,当a和b“足够接近”时,(a+b)²/4t将是π的近似值。我不确定“足够接近”是什么意思,但从我的测试来看,一次迭代得到2位数,两次迭代得到7位数,三次迭代得到15位数,当然这是双精度的,所以根据它的表示可能会有错误,TRUE计算可能会更准确。
最后,来点圆周率高尔夫(800位)怎么样?160个字符!
bogh5gae2#
我真的很喜欢这个程序,因为它通过查看自己的区域来近似π。
IOCCC 1988:westley.c
92dk7w1h3#
以下是我在高中学到的一种计算圆周率的技术的总体描述。
我之所以分享这一点,是因为我认为它足够简单,任何人都可以无限期地记住它,而且它还教会了你“蒙特卡洛”方法的概念--这是一种统计方法,可以得出看起来不是立即可以通过随机过程推断的答案。
画一个正方形,并在该正方形(半径等于正方形边的象限)内刻上一个象限(半圆的四分之一),这样它就可以填满尽可能多的正方形。
现在向正方形扔一个飞镖,并记录它落在哪里--也就是说,在正方形内的任何地方选择一个随机点。当然,它落在了正方形内,但它是在半圆形内吗?记录下这一事实。
重复这个过程很多次,你会发现半圆内的点数与抛出的总点数之比,称为x。
因为正方形的面积是r乘r,所以你可以推论出半圆的面积是x乘r乘r(即x乘r的平方)。因此,x乘以4会得到圆周率。
这不是一种快速使用的方法。但这是蒙特卡罗方法的一个很好的例子。如果你环顾四周,你会发现许多计算能力之外的问题都可以用这种方法解决。
ie3xauqp4#
出于完整性的考虑,C++模板版本,对于优化的构建,它将在编译时计算PI的近似值,并内联到单个值。
注意:对于i>10,优化的构建可能会很慢,对于非优化的运行也是如此。在12次迭代中,我相信大约有80k次对value()的调用(在没有记忆的情况下)。
ikfrs5lh5#
以下答案准确地回答了如何以尽可能快的方式--以最少的计算工作量做到这一点。即使你不喜欢这个答案,你也不得不承认,这确实是获得PI值的最快方法。
获取PI值的最快方法是:
1.选择你最喜欢的编程语言
1.加载其数学库
1.发现已经在那里定义了PI--可以使用了!
如果你手头没有数学库..
第二快方式(更通用的解决方案)是:
在互联网上查找PI,例如:
http://www.eveandersson.com/pi/digits/1000000(100万位数字。您的浮点精度是多少?)
或者在这里:
http://3.141592653589793238462643383279502884197169399375105820974944592.com/
或者在这里:
http://en.wikipedia.org/wiki/Pi
无论您想要使用什么精度的算术,找到所需的位数都非常快,并且通过定义一个常量,您可以确保不会浪费宝贵的CPU时间。
这不仅是一个部分幽默的答案,而且在现实中,如果有人继续在实际应用程序中计算PI的值。这将是对CPU时间的极大浪费,不是吗?至少我没有看到一个真正的应用程序来尝试重新计算这一点。
还要考虑NASA在计算星际旅行时只使用15位PI:
尊敬的版主:请注意,操作员问:“以最快的方式获得PI的价值”
wlzqhblo6#
实际上,有一整本书(其中包括)专门介绍计算圆周率的“快速”方法:乔纳森和彼得·博尔文(available on Amazon)的“圆周率与年率”(Pi And The AGM)。
我对年度股东大会和相关算法研究了很多:它很有趣(尽管有时不是微不足道的)。
请注意,要实现大多数现代算法来计算pi,您将需要一个多精度算术库(GMP是一个很好的选择,尽管我上次使用它已经有一段时间了)。
最好的算法的时间复杂度为O(M(N)log(N)),其中M(N)是两个n位整数(M(N)=O(nlog(N)log(log(N)使用基于FFT的算法相乘的时间复杂度,这种算法通常是在计算pi的位数时需要的,这种算法在GMP中实现)。
请注意,即使算法背后的数学运算可能不是微不足道的,算法本身通常是几行伪代码,并且它们的实现通常非常简单(如果您选择不编写自己的多精度算术:-))。
ni65a41a7#
BBP formula允许您计算第n位-以2(或16)为基数-甚至不必费心先计算前n-1位:)
carvr3hs8#
我没有将pi定义为常量,而是始终使用
acos(-1)
。yi0zb3m49#
这是一种“经典”方法,非常容易实现。这个用Python(不是最快的语言)实现的实现可以做到:
您可以找到更多信息here。
无论如何,要获得您想要的精确的pi值,最快的方法是:
下面是gmpy pi方法的一段源代码,我认为代码不如本例中的注解有用:
**编辑:**我在剪切粘贴和缩进方面遇到了一些问题,你可以找到源代码here。
ou6hu8tu10#
如果您所说的最快是指输入代码的最快,下面是golfscript解决方案:
xqkwcwgp11#
如果您愿意使用近似值,
355 / 113
适用于6位小数,并且具有可用于整数表达式的附加优势。如今,这一点已经不那么重要了,因为“浮点数学协处理器”已经不再有任何意义,但它曾经相当重要。kzipqqlq12#
使用类似机器的公式
在方案中实施,例如:
(+ (- (+ (* 176 (atan (/ 1 57))) (* 28 (atan (/ 1 239)))) (* 48 (atan (/ 1 682)))) (* 96 (atan (/ 1 12943))))
iyfamqjs13#
圆周率正好是3![Flink教授(《辛普森一家》)]
这是个笑话,但这里有一个是用C#编写的(需要.NET框架)。
6mw9ycah14#
双人套餐:
这将精确到小数点后14位,足以填满一个双精度(不准确可能是因为弧切线中的其余小数被截断)。
还有赛斯,它是3.141592653589793238463,不是64。
iq0todco15#
用D.计算编译时的PI
(从DSource.org复制)