Chr*_*ung 315 language-agnostic unix algorithm performance pi
我正在寻找获得π值的最快方法,作为个人挑战.更具体地说,我使用的方法不涉及使用#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("%s\t=> %e, time => %f\n", #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;
}
Run Code Online (Sandbox Code Playgroud)
内联汇编的东西(fldpi.c)只适用于x86和x64系统:
double
fldpi()
{
double pi;
asm("fldpi" : "=t" (pi));
return pi;
}
Run Code Online (Sandbox Code Playgroud)
以及构建我正在测试的所有配置的构建脚本(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
Run Code Online (Sandbox Code Playgroud)
除了在各种编译器标志之间进行测试之外(我也将32位与64位进行比较,因为优化不同),我也试过切换测试的顺序.但是,这个atan2(0, -1)版本每次仍然排在首位.
nlu*_*oni 201
在蒙特卡罗方法,如前所述,适用于一些伟大的概念,但它是,显然,不是最快的,而不是由一个长镜头,没有任何合理的措施.此外,这一切都取决于您正在寻找什么样的准确性.我所知道的最快的π是硬编码的数字.看看Pi和Pi [PDF],有很多公式.
这是一种快速收敛的方法 - 每次迭代大约14位数.PiFast是目前最快的应用程序,它将此公式与FFT结合使用.我只会编写公式,因为代码很简单.这个公式几乎是由Ramanujan发现并由Chudnovsky发现的.实际上他是如何计算出数十亿个数字的 - 所以它不是一种无视的方法.该公式将快速溢出,并且由于我们正在划分阶乘,因此延迟此类计算以删除术语将是有利的.


哪里,

以下是布伦特 - 萨拉明算法.维基百科提到当a和b "足够接近"时,(a + b)²/ 4t将是π的近似值.我不确定"足够接近"意味着什么,但是从我的测试来看,一次迭代得到2位数,两次得到7位,三次得到15次,当然这是双打,所以根据它的表示可能有错误在真正的计算可以更准确.
let pi_2 iters =
let rec loop_ a b t p i =
if i = 0 then a,b,t,p
else
let a_n = (a +. b) /. 2.0
and b_n = sqrt (a*.b)
and p_n = 2.0 *. p in
let t_n = t -. (p *. (a -. a_n) *. (a -. a_n)) in
loop_ a_n b_n t_n p_n (i - 1)
in
let a,b,t,p = loop_ (1.0) (1.0 /. (sqrt 2.0)) (1.0/.4.0) (1.0) iters in
(a +. b) *. (a +. b) /. (4.0 *. t)
Run Code Online (Sandbox Code Playgroud)
最后,一些pi高尔夫球(800位数)怎么样?160个字符!
int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5;for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);}
Run Code Online (Sandbox Code Playgroud)
Pat*_*Pat 113
我真的很喜欢这个程序,因为它通过查看自己的区域来近似π.
IOCCC 1988:westley.c
Run Code Online (Sandbox Code Playgroud)#define _ -F<00||--F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ }
Leo*_*ick 77
以下是我在高中学习计算pi的技术的一般描述.
我只是分享这个,因为我觉得很简单,任何人都可以无限期地记住它,而且它会教你"蒙特卡罗"方法的概念 - 这是一种统计方法,可以得到看起来不是很明显的答案.通过随机过程进行推理.
画一个正方形,并在该正方形内刻一个象限(四分之一半圆)(一个半径等于正方形边的象限,所以它尽可能多地填充正方形)
现在在广场上投掷飞镖,并记录它落在哪里 - 也就是说,在广场内的任何地方选择一个随机点.当然,它降落在广场内,但它是否在半圆内?记录这个事实.
多次重复这个过程 - 你会发现半圆内的点数与抛出的总数之比,称为这个比率x.
由于正方形的面积是r乘以r,因此可以推断出半圆的面积是r乘以r的x倍(即x乘以r的平方).因此x乘4将给你pi.
这不是一种快速使用的方法.但这是蒙特卡罗方法的一个很好的例子.如果你环顾四周,你可能会发现,除了你的计算技能之外的许多问题都可以通过这些方法解决.
jon*_*son 55
为了完整性,需要一个C++模板版本,对于优化构建,它将在编译时计算PI的近似值,并将内联到单个值.
#include <iostream>
template<int I>
struct sign
{
enum {value = (I % 2) == 0 ? 1 : -1};
};
template<int I, int J>
struct pi_calc
{
inline static double value ()
{
return (pi_calc<I-1, J>::value () + pi_calc<I-1, J+1>::value ()) / 2.0;
}
};
template<int J>
struct pi_calc<0, J>
{
inline static double value ()
{
return (sign<J>::value * 4.0) / (2.0 * J + 1.0) + pi_calc<0, J-1>::value ();
}
};
template<>
struct pi_calc<0, 0>
{
inline static double value ()
{
return 4.0;
}
};
template<int I>
struct pi
{
inline static double value ()
{
return pi_calc<I, I>::value ();
}
};
int main ()
{
std::cout.precision (12);
const double pi_value = pi<10>::value ();
std::cout << "pi ~ " << pi_value << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
注意,对于I> 10,优化的构建可能很慢,同样适用于非优化的运行.对于12次迭代,我相信有大约80k次调用value()(在没有memoisation的情况下).
Oys*_*erD 43
实际上有一本全书专门用于计算\ pi:'Pi和AGM'的快速方法,由Jonathan和Peter Borwein(在亚马逊上提供).
我研究了AGM和相关的算法:它非常有趣(尽管有时非常重要).
请注意,要实现大多数现代算法来计算\ pi,您需要一个多精度算术库(GMP是一个相当不错的选择,尽管自从我上次使用它以来已经有一段时间了).
最佳算法的时间复杂度在O(M(n)log(n))中,其中M(n)是两个n位整数相乘的时间复杂度(M(n)= O(n) log(n)log(log(n)))使用基于FFT的算法,这在计算\ pi的数字时通常是需要的,并且这种算法在GMP中实现).
请注意,即使算法背后的数学可能不是微不足道的,算法本身通常只有几行伪代码,并且它们的实现通常非常简单(如果您选择不编写自己的多精度算法:-)).
Til*_*ilo 42
以下以最快的方式精确地解决了如何做到这一点 - 以最少的计算工作量.即使你不喜欢这个答案,你也必须承认这确实是获得PI价值的最快方法.
该最快获得Pi值的方法是:
1)选择你喜欢的编程语言2)加载它的数学库3)并发现Pi已在那里定义 - 准备好使用!
如果您手头没有数学库..
在第二快的方式(更通用的解决方案)是:
在互联网上查找Pi,例如:
http://www.eveandersson.com/pi/digits/1000000(1百万位...你的浮点精度是多少?)
或者在这里:
http://3.141592653589793238462643383279502884197169399375105820974944592.com/
或者在这里:
http://en.wikipedia.org/wiki/Pi
找到你想要使用的精确算术所需的数字真的很快,通过定义一个常量,你可以确保不浪费宝贵的CPU时间.
这不仅是一个部分幽默的答案,而且实际上,如果有人继续在实际应用程序中计算Pi的价值......那将是一个相当大的浪费CPU时间,不是吗?至少我没有看到尝试重新计算它的真实应用程序.
亲爱的主持人:请注意OP问:"获取PI价值的最快方式"
小智 23
我总是使用而不是将pi定义为常量acos(-1).
And*_*mbu 21
这是一种"经典"方法,非常容易实现.这个实现,在python(不是那么快的语言)中做到了:
from math import pi
from time import time
precision = 10**6 # higher value -> higher precision
# lower value -> higher speed
t = time()
calc = 0
for k in xrange(0, precision):
calc += ((-1)**k) / (2*k+1.)
calc *= 4. # this is just a little optimization
t = time()-t
print "Calculated: %.40f" % calc
print "Costant pi: %.40f" % pi
print "Difference: %.40f" % abs(calc-pi)
print "Time elapsed: %s" % repr(t)
Run Code Online (Sandbox Code Playgroud)
您可以在此处找到更多信息.
无论如何,在python中获得精确的你想要的pi值的最快方法是:
from gmpy import pi
print pi(3000) # the rule is the same as
# the precision on the previous code
Run Code Online (Sandbox Code Playgroud)
这里是gmpy pi方法的源代码,我不认为代码在这种情况下与注释一样有用:
static char doc_pi[]="\
pi(n): returns pi with n bits of precision in an mpf object\n\
";
/* This function was originally from netlib, package bmp, by
* Richard P. Brent. Paulo Cesar Pereira de Andrade converted
* it to C and used it in his LISP interpreter.
*
* Original comments:
*
* sets mp pi = 3.14159... to the available precision.
* uses the gauss-legendre algorithm.
* this method requires time o(ln(t)m(t)), so it is slower
* than mppi if m(t) = o(t**2), but would be faster for
* large t if a faster multiplication algorithm were used
* (see comments in mpmul).
* for a description of the method, see - multiple-precision
* zero-finding and the complexity of elementary function
* evaluation (by r. p. brent), in analytic computational
* complexity (edited by j. f. traub), academic press, 1976, 151-176.
* rounding options not implemented, no guard digits used.
*/
static PyObject *
Pygmpy_pi(PyObject *self, PyObject *args)
{
PympfObject *pi;
int precision;
mpf_t r_i2, r_i3, r_i4;
mpf_t ix;
ONE_ARG("pi", "i", &precision);
if(!(pi = Pympf_new(precision))) {
return NULL;
}
mpf_set_si(pi->f, 1);
mpf_init(ix);
mpf_set_ui(ix, 1);
mpf_init2(r_i2, precision);
mpf_init2(r_i3, precision);
mpf_set_d(r_i3, 0.25);
mpf_init2(r_i4, precision);
mpf_set_d(r_i4, 0.5);
mpf_sqrt(r_i4, r_i4);
for (;;) {
mpf_set(r_i2, pi->f);
mpf_add(pi->f, pi->f, r_i4);
mpf_div_ui(pi->f, pi->f, 2);
mpf_mul(r_i4, r_i2, r_i4);
mpf_sub(r_i2, pi->f, r_i2);
mpf_mul(r_i2, r_i2, r_i2);
mpf_mul(r_i2, r_i2, ix);
mpf_sub(r_i3, r_i3, r_i2);
mpf_sqrt(r_i4, r_i4);
mpf_mul_ui(ix, ix, 2);
/* Check for convergence */
if (!(mpf_cmp_si(r_i2, 0) &&
mpf_get_prec(r_i2) >= (unsigned)precision)) {
mpf_mul(pi->f, pi->f, r_i4);
mpf_div(pi->f, pi->f, r_i3);
break;
}
}
mpf_clear(ix);
mpf_clear(r_i2);
mpf_clear(r_i3);
mpf_clear(r_i4);
return (PyObject*)pi;
}
Run Code Online (Sandbox Code Playgroud)
编辑:我有剪切和粘贴和标识的问题,无论如何你可以在这里找到源.
Mic*_*are 20
如果最快你的意思是最快输入代码,这里是golfscript解决方案:
;''6666,-2%{2+.2/@*\/10.3??2*+}*`1000<~\;
Run Code Online (Sandbox Code Playgroud)
小智 18
使用类似Machin的公式
176 * arctan (1/57) + 28 * arctan (1/239) - 48 * arctan (1/682) + 96 * arctan(1/12943)
[; \left( 176 \arctan \frac{1}{57} + 28 \arctan \frac{1}{239} - 48 \arctan \frac{1}{682} + 96 \arctan \frac{1}{12943}\right) ;], for you TeX the World people.
Run Code Online (Sandbox Code Playgroud)
在Scheme中实现,例如:
(+ (- (+ (* 176 (atan (/ 1 57))) (* 28 (atan (/ 1 239)))) (* 48 (atan (/ 1 682)))) (* 96 (atan (/ 1 12943))))
Dan*_*ral 17
如果您愿意使用近似值,355 / 113则适用于6位十进制数字,并且具有可用于整数表达式的附加优势.这些日子并不重要,因为"浮点数学协处理器"不再具有任何意义,但它曾经非常重要.
小智 16
Pi正好是3![教授 弗林克(辛普森一家)]
笑话,但这是C#中的一个(需要.NET-Framework).
using System;
using System.Text;
class Program {
static void Main(string[] args) {
int Digits = 100;
BigNumber x = new BigNumber(Digits);
BigNumber y = new BigNumber(Digits);
x.ArcTan(16, 5);
y.ArcTan(4, 239);
x.Subtract(y);
string pi = x.ToString();
Console.WriteLine(pi);
}
}
public class BigNumber {
private UInt32[] number;
private int size;
private int maxDigits;
public BigNumber(int maxDigits) {
this.maxDigits = maxDigits;
this.size = (int)Math.Ceiling((float)maxDigits * 0.104) + 2;
number = new UInt32[size];
}
public BigNumber(int maxDigits, UInt32 intPart)
: this(maxDigits) {
number[0] = intPart;
for (int i = 1; i < size; i++) {
number[i] = 0;
}
}
private void VerifySameSize(BigNumber value) {
if (Object.ReferenceEquals(this, value))
throw new Exception("BigNumbers cannot operate on themselves");
if (value.size != this.size)
throw new Exception("BigNumbers must have the same size");
}
public void Add(BigNumber value) {
VerifySameSize(value);
int index = size - 1;
while (index >= 0 && value.number[index] == 0)
index--;
UInt32 carry = 0;
while (index >= 0) {
UInt64 result = (UInt64)number[index] +
value.number[index] + carry;
number[index] = (UInt32)result;
if (result >= 0x100000000U)
carry = 1;
else
carry = 0;
index--;
}
}
public void Subtract(BigNumber value) {
VerifySameSize(value);
int index = size - 1;
while (index >= 0 && value.number[index] == 0)
index--;
UInt32 borrow = 0;
while (index >= 0) {
UInt64 result = 0x100000000U + (UInt64)number[index] -
value.number[index] - borrow;
number[index] = (UInt32)result;
if (result >= 0x100000000U)
borrow = 0;
else
borrow = 1;
index--;
}
}
public void Multiply(UInt32 value) {
int index = size - 1;
while (index >= 0 && number[index] == 0)
index--;
UInt32 carry = 0;
while (index >= 0) {
UInt64 result = (UInt64)number[index] * value + carry;
number[index] = (UInt32)result;
carry = (UInt32)(result >> 32);
index--;
}
}
public void Divide(UInt32 value) {
int index = 0;
while (index < size && number[index] == 0)
index++;
UInt32 carry = 0;
while (index < size) {
UInt64 result = number[index] + ((UInt64)carry << 32);
number[index] = (UInt32)(result / (UInt64)value);
carry = (UInt32)(result % (UInt64)value);
index++;
}
}
public void Assign(BigNumber value) {
VerifySameSize(value);
for (int i = 0; i < size; i++) {
number[i] = value.number[i];
}
}
public override string ToString() {
BigNumber temp = new BigNumber(maxDigits);
temp.Assign(this);
StringBuilder sb = new StringBuilder();
sb.Append(temp.number[0]);
sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);
int digitCount = 0;
while (digitCount < maxDigits) {
temp.number[0] = 0;
temp.Multiply(100000);
sb.AppendFormat("{0:D5}", temp.number[0]);
digitCount += 5;
}
return sb.ToString();
}
public bool IsZero() {
foreach (UInt32 item in number) {
if (item != 0)
return false;
}
return true;
}
public void ArcTan(UInt32 multiplicand, UInt32 reciprocal) {
BigNumber X = new BigNumber(maxDigits, multiplicand);
X.Divide(reciprocal);
reciprocal *= reciprocal;
this.Assign(X);
BigNumber term = new BigNumber(maxDigits);
UInt32 divisor = 1;
bool subtractTerm = true;
while (true) {
X.Divide(reciprocal);
term.Assign(X);
divisor += 2;
term.Divide(divisor);
if (term.IsZero())
break;
if (subtractTerm)
this.Subtract(term);
else
this.Add(term);
subtractTerm = !subtractTerm;
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 16
双打:
4.0 * (4.0 * Math.Atan(0.2) - Math.Atan(1.0 / 239.0))
Run Code Online (Sandbox Code Playgroud)
这将精确到14个小数位,足以填充一个双倍(不准确可能是因为弧切线中的其余小数被截断).
Seth,它是3.14159265358979323846 3,而不是64.
Bra*_*ert 15
(从DSource.org复制)
/** Calculate pi at compile time
*
* Compile with dmd -c pi.d
*/
module calcpi;
import meta.math;
import meta.conv;
/** real evaluateSeries!(real x, real metafunction!(real y, int n) term)
*
* Evaluate a power series at compile time.
*
* Given a metafunction of the form
* real term!(real y, int n),
* which gives the nth term of a convergent series at the point y
* (where the first term is n==1), and a real number x,
* this metafunction calculates the infinite sum at the point x
* by adding terms until the sum doesn't change any more.
*/
template evaluateSeries(real x, alias term, int n=1, real sumsofar=0.0)
{
static if (n>1 && sumsofar == sumsofar + term!(x, n+1)) {
const real evaluateSeries = sumsofar;
} else {
const real evaluateSeries = evaluateSeries!(x, term, n+1, sumsofar + term!(x, n));
}
}
/*** Calculate atan(x) at compile time.
*
* Uses the Maclaurin formula
* atan(z) = z - z^3/3 + Z^5/5 - Z^7/7 + ...
*/
template atan(real z)
{
const real atan = evaluateSeries!(z, atanTerm);
}
template atanTerm(real x, int n)
{
const real atanTerm = (n & 1 ? 1 : -1) * pow!(x, 2*n-1)/(2*n-1);
}
/// Machin's formula for pi
/// pi/4 = 4 atan(1/5) - atan(1/239).
pragma(msg, "PI = " ~ fcvt!(4.0 * (4*atan!(1/5.0) - atan!(1/239.0))) );
Run Code Online (Sandbox Code Playgroud)
Jos*_*ons 13
这个版本(在Delphi中)并不特别,但它至少比Nick Hodge在他博客上发布的版本更快:).在我的机器上,进行十亿次迭代需要大约16秒,给出值3.14159265 25879(准确部分以粗体显示).
program calcpi;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
start, finish: TDateTime;
function CalculatePi(iterations: integer): double;
var
numerator, denominator, i: integer;
sum: double;
begin
{
PI may be approximated with this formula:
4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 .......)
//}
numerator := 1;
denominator := 1;
sum := 0;
for i := 1 to iterations do begin
sum := sum + (numerator/denominator);
denominator := denominator + 2;
numerator := -numerator;
end;
Result := 4 * sum;
end;
begin
try
start := Now;
WriteLn(FloatToStr(CalculatePi(StrToInt(ParamStr(1)))));
finish := Now;
WriteLn('Seconds:' + FormatDateTime('hh:mm:ss.zz',finish-start));
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Run Code Online (Sandbox Code Playgroud)
Kri*_*son 12
在过去,使用小字大小和缓慢或不存在的浮点运算,我们曾经做过这样的事情:
/* Return approximation of n * PI; n is integer */
#define pi_times(n) (((n) * 22) / 7)
Run Code Online (Sandbox Code Playgroud)
对于不需要很高精度的应用程序(例如视频游戏),这非常快且足够准确.
Set*_*eth 12
如果要计算 π值的近似值(由于某种原因),则应尝试二进制提取算法.Bellard对BBP 的改进给出了O(N ^ 2)中的PI.
如果要获得 π值的近似值来进行计算,则:
PI = 3.141592654
Run Code Online (Sandbox Code Playgroud)
当然,这只是一个近似值,并不完全准确.它的价格略高于0.00000000004102.(四个万亿分之一,大约4/10,000,000,000).
如果你想用π 做数学,那么给自己一个铅笔和纸或计算机代数包,并使用π的精确值π.
如果你真的想要一个公式,这个很有趣: