关于罗塞塔石:代码高尔夫:Pi的莱布尼兹公式

Code Golf: Leibniz formula for Pi

我最近在"你更具争议的编程观点是什么"中发布了我最喜欢的访谈白板编码问题之一,它是用来编写一个函数,用莱布尼兹公式计算圆周率。

它可以以多种不同的方式接近,退出条件需要考虑一点,所以我认为它可能会成为一个有趣的代码高尔夫问题。最短的代码获胜!

Given that Pi can be estimated using the function 4 * (1 - 1/3 + 1/5 - 1/7 + ...) with more terms giving greater accuracy, write a function that calculates Pi to within 0.00001.

编辑:2008年1月3日

正如我在评论中所建议的,我将退出条件改为0.00001以内,因为这就是我真正的意思(精确到小数点后5位由于四舍五入的原因要困难得多,所以我不想在面试中问这个问题,而在0.00001以内更容易理解和实现退出条件)。

另外,为了回答这些评论,我想我的意图是解决方案应该计算迭代次数,或者在已经做了足够多的时候进行检查,但是没有什么可以阻止您预先计算迭代次数并使用这个数字。我真的出于兴趣问了这个问题,想看看人们会想出什么办法。


J,14字符

1
4*-/%>:+:i.1e6

解释

  • 1e6是数字1,后跟6个零(1000000)。
  • i.y生成第一个y非负数。
  • +:是一个函数,它使list参数中的每个元素加倍。
  • >:是一个函数,它在list参数中每增加一个元素。

因此,表达式>:+:i.1e6生成前一百万个奇数:

1 3 5 7 ...

  • %是倒数运算符(分子"1"可以省略)。
  • -/对list参数中的每个元素进行交替求和。

因此,表达式-/%>:+:i.1e6生成前一百万奇数倒数的交替和:

1 - 1/3 + 1/5 - 1/7 + ...

  • 4*乘以4。如果把前面的和乘以四,就得到π。

就是这样!J是一种强大的数学语言。

编辑:从生成9开始!(362880)替代和的术语足以具有5个十进制数字的精度,并且由于莱布尼兹公式也可以这样写:

4 - 4/3 + 4/5 - 4/7 + ...

…您可以编写一个较短的12个字符版本的程序:

1
-/4%>:+:i.9!


语言:brainfuck,char数:51 / 59

对这个数?= = = = =】

因为那里是没有点数量在brainfuck floating -,这是很难获得的divisions正确地工作。GRR。

无newline(51):

1
+++++++[>+++++++<-]>++.-----.+++.+++.---.++++.++++.

与newline(59):

1
+++++++[>+++++++>+<<-]>++.-----.+++.+++.---.++++.++++.>+++.


珀尔26字符

26只是函数,27要计算,31要打印。从评论到这个答案。

1
2
3
sub _{$-++<1e6&&4/$-++-&_}       # just the sub
sub _{$-++<1e6&&4/$-++-&_}_      # compute
sub _{$-++<1e6&&4/$-++-&_}say _  # print

28字符

28只计算,34只打印。从评论中。请注意,此版本不能使用"say"。

1
2
$.=.5;$\=2/$.++-$\for 1..1e6        # no print
$.=.5;$\=2/$.++-$\for$...1e6;print  # do print, with bonus obfuscation

36字符

36只计算,42只打印。哈德逊从评论中接受了德雷夫斯的重新安排。

1
2
$/++;$\+=8/$//($/+2),$/+=4for$/..1e6
$/++;$\+=8/$//($/+2),$/+=4for$/..1e6;print

关于迭代计数:就我的数学记忆而言,400000可以证明精确到0.00001。但是一百万(或者低到8e5)实际上使小数展开式匹配5个小数点,并且它是相同的字符计数,所以我保留了它。


红宝石,33特点

1
(0..1e6).inject{|a,b|2/(0.5-b)-a}


另一个C版本:

(60个字符)

1
4*Enumerable.Range(0, 500000).Sum(x => Math.Pow(-1, x)/(2*x + 1));  // = 3,14159


在python中有52个字符:

1
print 4*sum(((-1.)**i/(2*i+1)for i in xrange(5**8)))

(51从xrange中删除"x"。)

以八度(或matlab)表示的36个字符:

1
l=0:5^8;disp((-1).^l*(4./(2.*l+1))')

(执行"format long";显示所有有效数字。)省略"disp",我们将达到30个字符:

1
2
octave:5> l=0:5^8;(-1).^l*(4./(2.*l+1))'
ans = 3.14159009359631


Oracle SQL 73字符

1
select -4*sum(power(-1,level)/(level*2-1)) from dual connect by level<1e6


语言:C,字符数:71

1
2
float p;main(i){for(i=1;1E6/i>5;i+=2)p-=(i%4-2)*4./i;printf("%g
",p);}

语言:C99,字符数:97(包括必需的换行符)

1
2
3
#include <stdio.h>
float p;int main(){for(int i=1;1E6/i>5;i+=2)p-=(i%4-2)*4./i;printf("%g
",p);}

我应该注意到,上面的版本(相同)跟踪额外的迭代是否会影响结果。因此,它执行的操作数量最少。要添加更多的数字,请将1E6替换为1E(num_digits+1)4E5替换为4E(num_digits)(取决于版本)。对于完整的程序,可能需要更换%gfloat也可能需要改为double

语言:C,字符数:67(见注释)

1
2
double p,i=1;main(){for(;i<1E6;i+=4)p+=8/i/(i+2);printf("%g
",p);}

这个版本使用了一个已发布算法的修改版本,其他一些答案也使用了这个版本。此外,它不像前两个解决方案那样干净/高效,因为它强制100000次迭代,而不是在迭代变得毫无意义时检测。

语言:C,字符数:24(作弊)

1
main(){puts("3.14159");}

但不适用于数字计数大于6的情况。


23个字符,在matlab中:

1
a=1e6;sum(4./(1-a:4:a))


Haskell

我得到了它在大人物:34

1
foldl subtract 4$map(4/)[3,5..9^6]

这种表达3.141596416935556当产量评估。

编辑:这是一个稍短的版本(在33特点),而不是使用foldl1 foldl:

1
foldl1 subtract$map(4/)[1,3..9^6]

编辑2、9 6 6而不是10 ^ ^。已成为一个economical .)

三、编辑与foldl取代"和"与foldl1 foldl和foldl1 respectively &表示,作为一个结果的overflows编辑2,它不长。谢谢你为这个大shreevatsar noticing。


F:

尝试1:

1
let pi = 3.14159

作弊?不,它以风格取胜!

尝试2:

1
2
3
4
5
let pi =
    seq { 0 .. 100 }
    |> Seq.map (fun x -> float x)
    |> Seq.fold (fun x y -> x + (Math.Pow(-1.0, y)/(2.0 * y + 1.0))) 0.0
    |> (fun x -> x * 4.0)

代码>

它不是尽可能的紧凑,而是相当惯用的f。


普通Lisp,55个字符。

1
(loop for i from 1 upto 4e5 by 4 sum (/ 8d0 i (+ i 2)))


mathematica,27(arguably沙尔斯作为低为26,或作为高为33)

1
NSum[8/i/(i+2),{i,1,9^9,4}]

如果你删除"N"然后它最初的回答作为一个returns(巨大的)分数。

如果这是mathematica在线互动,并不需要一个大的打印输出结果,然后prepend项声明其"Print@沙尔斯"为总的33。

注:

如果它是大的数量hardcode在线互动的条件,那么我不认为有任何答案,但gotten这是正确的。当当前的检查是不threshold下面是一些比hardcoding的数量的术语。只是因为当前的长期变化是唯一的第六第七位数的平均或不顺的足够的条件,在不改变第五位数。


使用公式的错误为长期在安全alternating系列(和因此的必要数量的iterations以实现accuracy是不难的desired coded进入程序):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void Main(string[] args) {
    double tolerance = 0.000001;
    double piApproximation = LeibnizPi(tolerance);
    Console.WriteLine(piApproximation);
}

private static double LeibnizPi(double tolerance) {
    double quarterPiApproximation = 0;

    int index = 1;
    double term;
    int sign = 1;
    do {
        term = 1.0 / (2 * index - 1);
        quarterPiApproximation += ((double)sign) * term;
        index++;
        sign = -sign;
    } while (term > tolerance);

    return 4 * quarterPiApproximation;
}


Perl:

1
$i+=($_&1?4:-4)/($_*2-1)for 1..1e6;print$i

总共42个字符。


C:

1
2
3
4
5
6
7
8
9
10
11
public static double Pi()
{
    double pi = 0;
    double sign = 1;
    for (int i = 1; i < 500002; i += 2)
    {
        pi += sign / i;
        sign = -sign;
    }
    return 4 * pi;
}


F(交互模式)(59个字符)

1
{0.0..1E6}|>Seq.fold(fun a x->a+ -1.**x/(2.*x+1.))0.|>(*)4.

(发出警告,但忽略了投射)


Ruby,41个字符(使用irb):

1
s=0;(3..3e6).step(4){|i|s+=8.0/i/(i-2)};s

或者稍微长一点,非IRB版本:

1
s=0;(3..3e6).step(4){|i|s+=8.0/i/(i-2)};p s

这是修改后的莱布尼兹:

  • 组合成对的术语。这给你2/3+2/35+2/99+…
  • pi变为8*(1/(1*3)+1/(5*7)+1/(9*11)+…)

  • JavaScript:

    1
    a=0,b=-1,d=-4,c=1e6;while(c--)a+=(d=-d)/(b+=2)

    在JavaScript。51特点。但你不想obviously赢得。:P

    编辑- 46 -更新成为大人物strager现在,谢谢。:)

    更新(2010年3月30)

    一个faster(precise只有五大特点的地方decimal)43版由大卫默多克

    1
    for(a=0,b=1,d=4,c=~4e5;++c;d=-d)a-=d/(b-=2)


    这是治疗腮腺炎的方法。

    1
    2
    3
    4
    pi(N)
     N X,I
     S X=1 F I=3:4:N-2 S X=X-(1/I)+(1/(I+2))
     Q 4*X

    参数n表示要使用多少个重复分数。也就是说,如果你通过5,它将评估4*(1-1/3+1/5-1/7+1/9-1/11)

    一些经验测试表明,n=272241是最小值,当被截断为5个小数点时,给出了3.14159的正确值。您必须转到n=852365才能得到一个取整为3.14159的值。


    C:使用# iterator布拉克

    1
    2
    3
    4
    5
    6
    7
    8
    9
    static IEnumerable<double> Pi()
    {
        double i = 4, j = 1, k = 4;
        for (;;)
        {
            yield return k;
            k += (i *= -1) / (j += 2);
        }
    }

    对于记录,这个方案实现有95个字符,忽略了不必要的空白。

    1
    2
    3
    4
    5
    6
    (define (f)
      (define (p a b)
        (if (> a b)
          0
          (+ (/ 1.0 (* a (+ a 2))) (p (+ a 4) b))))
      (* 8 (p 1 1e6)))

    语言:c99(隐式返回0),字符数:99(95+4必需空格)

    退出条件取决于当前值,而不是固定计数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>

    float p, s=4, d=1;
    int main(void) {
      for (; 4/d > 1E-5; d += 2)
            p -= (s = -s) / d;
      printf("%g
    ", p);
    }

    压缩版本

    1
    2
    3
    4
    5
    #include<stdio.h>
    float
    p,s=4,d=1;int
    main(void){for(;4/d>1E-5;d+=2)p-=(s=-s)/d;printf("%g
    ",p);}


    这里有一个使用c_的递归答案。它只能在发布模式下使用X64 JIT,因为这是唯一应用尾调用优化的JIT,而且由于系列聚合速度太慢,因此会导致没有它的StackOverflowException

    IteratePi函数作为一个匿名lambda是很好的,但是由于它是自递归的,我们必须开始用y组合器做各种可怕的事情,所以我把它作为一个单独的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static double CalculatePi()
    {
        return IteratePi(0.0, 1.0, true);
    }

    private static double IteratePi(double result, double denom, bool add)
    {
        var term = 4.0 / denom;
        if (term < 0.00001) return result;    
        var next = add ? result + term : result - term;
        return IteratePi(next, denom + 2.0, !add);
    }


    大多数当前的答案,他们会assume 5号accuracy在一些地点的iterations这个数量是hardcoded进入程序。我的理解是研究问题的程序本身是应该想出去当是有答案accurate安全地点有五大和停止。对这一假设这里是我的# C解。我可不bothered的数量大的特点minimise自没有办法它可以与相互竞争的研究已经有了一些答案,所以我认为我会使它相反,可读性很强。:)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
        private static double GetPi()
        {
            double acc = 1, sign = -1, lastCheck = 0;

            for (double div = 3; ; div += 2, sign *= -1)
            {
                acc += sign / div;

                double currPi = acc * 4;
                double currCheck = Math.Round(currPi, 5);

                if (currCheck == lastCheck)
                    return currPi;

                lastCheck = currCheck;
            }
        }


    语言:DC,字符数:35

    1
    dc -e '9k0 1[d4r/r2+sar-lad274899>b]dsbxrp'


    红宝石:

    1
    2
    irb(main):031:0> 4*(1..10000).inject {|s,x| s+(-1)**(x+1)*1.0/(2*x-1)}
    => 3.14149265359003


    爪哇

    1
    2
    3
        double pi=0,s=-1,i=1;
        for (;i<1E6;i+=2)pi+=((1d/i)*(s=-s));
        pi*=4;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    double d = 1;
    double s = 1;
    double pi = 0;

    while(4.0 / d > 0.000001){
        pi += s*4.0/d;
        d+=2;
        s = -s;        
    }
    printf("%f
    ", pi);

    C:在线互动# 50沙尔斯

    1
    2
    3
    static single Pi(){
      return Math.Round(Math.PI, 5));
    }

    它只是说,"以写为帐户的一个函数的公式……"这不醉的programmatically)认为:reproduce公式外的盒……

    C:linq - 78 #沙尔斯

    1
    2
    static double pi = 4 * Enumerable.Range(0, 1000000)
                   .Sum(n => Math.Pow(-1, n) / (2 * n + 1));

    C:94个# alternate linq沙尔斯

    1
    2
    static double pi = return 4 * (from n in Enumerable.Range(0, 1000000)
                                   select Math.Pow(-1, n) / (2 * n + 1)).Sum();

    最后需要提到的是这和以前的算法相比,它mathematically condenses你不担心有关于保持变化的标志。

    C # longhand - 89(不counting沙尔斯unrequired空间):

    1
    2
    3
    4
    5
    6
    static double pi()
    {
      var t = 0D;
      for (int n = 0; n < 1e6; t += Math.Pow(-1, n) / (2 * n + 1), n++) ;
      return 4 * t;
    }


    64个字符在awk中:

    1
    2
    ~# awk 'BEGIN {p=1;for(i=3;i<10^6;i+=4){p=p-1/i+1/(i+2)}print p*4}'
    3.14159


    注意到这一点之后

    1
    2
    3
    (= (- (/ 4 n)
          (/ 4 (+ n 2)))
       (/ 8 n (+ n 2)))

    或者,用更熟悉的表示法:

    1
    2
    3
    4    4      8
    - - --- = ------
    n   n+2   n(n+2)

    普通lisp,带有do*循环(62个基本字符):

    1
    2
    3
    4
    (do* ((n 1 (+ n 4))
          (p 8/3 (+ p (/ 8 n (+ n 2)))))
         ((< (- pi p) 1e-6)
          p)

    带有尾部递归函数(70个基本字符):

    1
    2
    3
    4
    5
    6
    (defun l (n p)
      (if (< (- pi p) 1e-6)
          p
          (l (+ n 4)
              (+ p (/ 8 n (+ n 2))))))
    (l 1 0)

    以及扩展循环(86个基本字符):

    1
    2
    3
    4
    (loop for n from 1 by 4
          sum (/ 8 n (+ n 2)) into p
          until (< (- pi p) 1e-6)
          finally (return p))

    所有这些都是基于这样一个假设:为了达到预期的准确度,我们必须进行多少初步检查,这都是作弊。


    C++

    1
    2
    3
    4
    5
    6
    7
    double LeibnizPi( double tolerance )
    {
        double sum = 1.0;
        for( int plus_div = 5, minus_div = -3, limit = 10 / tolerance; plus_div < limit ; plus_div += 4, minus_div -= 4 )
            sum += 1./plus_div + 1./minus_div;
        return 4 * sum;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #!/usr/bin/env python
    from math import *
    denom = 1.0
    imm = 0.0
    sgn = 1
    it = 0
    for i in xrange(0, int(1e6)):
        imm += (sgn*1/denom)
        denom += 2
        sgn *= -1    
    print str(4*imm)


    这是我的C++,可能是最长的方法:P

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    double pi(){
       bool add = true;
       double rPi = 0;
       for(long i = 1; i < 99999999; i=i+2)
       {
                double y = (double) i;
                double x = (double) 1;
                if(add)
                {
                       rPi = rPi + (x/y);
                       add = false;
                }
                else
                {
                        rPi = rPi - (x/y);
                        add = true;
                }
       }
                return (rPi * (double) 4);
       }


    另一个vb聚集相当酷的解决方案,使用的语法:

    1
    2
    Public ReadOnly Pi As Double = 4 * Aggregate i In Enumerable.Range(0, 100000) _
                                       Select (-1) ^ i / (i * 2 + 1) Into Sum()

    特点:无unnecessary表达whitespaces 74只。


    Erlang,约126个字符:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    -module (pi).
    -export ([pi/0]).

    pi() -> 4 * pi(0,1,1).
    pi(T,M,D) ->
        A = 1 / D,
        if A > 0.00001
                  -> pi(T+(M*A), M*-1, D+2);
            true  -> T
        end.

    嗯……作为数字处理中的一个一般规则,我们应该从最小的项到最大的项求和,以避免精度损失带来的麻烦。所以在

    FrTr77

    精简(248个字符)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
          function pi(n)
          pi=0.
          t=10**(-n-0.5)
          i=int(4/t)
          i=i/2
          s=-1.                    
          do 1 j=i*2+1,1,-2
             pi = pi + s/j
             s=-s
     1    continue
          pi=abs(pi)*4              
          return
          end

    带脚手架和注释(600个字符)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
          program leibnitz

          n=5
          p=int(pi(n)*10.**n)/10.**n
          write(6,*)p

          stop
          end

    c     Returns pi computed to <n> digits by the leibniz formula
          function pi(n)
          pi=0.
    c     find the smallest term we need by insuring that it is too small to
    c     effect the interesting digits.
          t=10**(-n-0.5)
          i=int(4/t)
          i=i/2
          s=-1.                     ! sign of term, might be off by one, but
          do 1 j=i*2+1,1,-2
             pi = pi + s/j
             s=-s
     1    continue
          pi=abs(pi)*4              ! we fix the sign problem here
          return
          end

    输出:

    1
       3.1415901

    它似乎适用于高达6ish的任意数字数,其中real的精度已耗尽。它没有针对速度或最小操作次数进行优化。


    爪哇

    1
    2
    3
    4
    5
    void pi(){
        double x=1,y=1,d=1;
        for(;x<1E6;) { y=-y;d+=y/((2*x++)+1); }
        System.out.println(d*4);
    }

    python 3(40字节)

    1
    sum(8/(n*(n+2))for n in range(1,5**8,4))

    此版本使用来自@svante答案的优化。

    打印+ 7字节

    1
    print(sum(8/(n*(n+2))for n in range(1,5**8,4)))

    python 2.x+1字节

    1
    sum(8./(n*(n+2))for n in range(1,5**8,4))

    打印+ 6字节

    1
    print sum(8./(n*(n+2))for n in range(1,5**8,4))

    http://codepad.org/amtxuxkp


    Lua,46个字符

    1
    p=4 for i=3,9^6,4 do p=p-8/i/(i+2)end print(p)

    117、vb沙尔斯

    1
    2
    3
    4
    5
    6
    7
    Function Pi()
      Dim t = 0D
      For n = 0 To 1000000
        t += Math.Pow(-1, n) / (2 * n + 1)
      Next
      Return 4 * t
    End Function

    vb linq 115沙尔斯(omitting延续的unnecessary线):

    1
    2
    3
    4
    Function Pi()
      Return 4 * Enumerable.Range(0, 1000000) _
                 .Sum(Function(n) Math.Pow(-1, n) / (2 * n + 1))
    End Function

    然后调用:

    1
    2
    3
    Sub Main()
      Console.WriteLine("{0:n5}", Pi)
    End Sub

    R—27字符

    1
    sum(4/seq(1,1e6,2)*c(1,-1))

    PHP,99字符

    $output =

    ${!${''}=function(){$pi=1;for($i=0;$i

    var_dump($output);

    我想有一些我不知道的技巧来减少这个答案!从这篇文章中获得了一行输出技巧。


    我只是在阅读了关于有争议意见的话题中的采访问题之后就写了这篇文章。它不漂亮,但我花了3-4分钟,我正在检查每个循环的准确性。C++。明天我会醒来,然后贴出一个不烂的解决方案:)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    double get_pi(int acc)
    {

      double pi;
      double dynamicpart;
      int operationCoeff = 1;
      int denom = 3;
      while(1)
      {
          dynamicpart =
             1/denom + operationCoeff*(denom+2);
          pi = 4*(1-dynamicpart);
          if(!(pi*acc*10-(int)pi*acc*10)) break;
    )
          denom+=2;
          operationCoeff = -operationCoeff;
      }



    }

    1字:.写在"我的自上而下的检查语言,只有一个答案,没有其他"。

    是的,这是一个玩笑,但说真的,除非你不允许DSL,否则每一个代码高尔夫比赛都会被一些写自己的语言的goober赢得,他用一个字符返回一个结果…