Code Golf:Pi的Leibniz公式

时间:2009-01-02 17:42:46

标签: code-golf rosetta-stone

我最近在“one of my favourite interview whiteboard coding questions”中发布了What's your more controversial programming opinion,即编写一个使用Leibniz formula计算Pi的函数。

它可以通过多种不同的方式进行处理,退出条件需要一些思考,所以我认为这可能会成为一个有趣的代码高尔夫问题。最短的代码获胜!

  

鉴于Pi可以使用函数4 *(1 - 1/3 + 1/5 - 1/7 + ...)进行估算,其中更多项可以提供更高的精度,请编写一个计算Pi到0.00001以内的函数。

编辑:2008年1月3日

正如评论中所建议的那样,我将退出条件更改为0.00001,因为这就是我的真实含义(由于四舍五入,精确度小数点后五位更难,所以我不想在面试中问这个问题,而在0.00001之内更容易理解并实现退出条件。)

另外,为了回答这些评论,我想我的意图是解决方案应该计算迭代次数,或检查它何时做得足够多,但是没有什么能阻止你预先计算迭代次数并使用它数。我真的有兴趣问这个问题,看看人们会想出什么。

46 个答案:

答案 0 :(得分:61)

J,14个字符

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

说明

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

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

  

1 3 5 7 ...

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

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

  

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

  • 4*乘以4。如果你将前一个总和乘以4,则得到π。

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


编辑:自生成9! (362880)交替金额的条款足以具有5位十进制数字,因为莱布尼兹公式也可以这样写:

  

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

...你可以写一个较短的, 12个字符版本的程序:

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

答案 1 :(得分:37)

语言:Brainfuck,Char count:51/59

这算得上吗? =]

因为Brainfuck中没有浮点数,所以很难让分区正常工作。 GRR。

没有换行符(51):

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

使用换行符(59):

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

答案 2 :(得分:24)

的Perl

26个字符

26只是函数,27来计算,31来打印。从评论到this answer

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

28个字符

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

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

36个字符

36只是计算,42来打印。哈德森对评论的重新安排采取了重新安排。

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

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

答案 3 :(得分:23)

Ruby,33个字符

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

答案 4 :(得分:20)

另一个C#版本:

(60个字符)

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

答案 5 :(得分:14)

Python 中的52个字符:

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

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

Octave(或Matlab)中的36个字符:

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

(执行“format long;”以显示所有有效数字。)省略'disp'我们达到30个字符:

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

答案 6 :(得分:13)

Oracle SQL 73个字符

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

答案 7 :(得分:10)

MATLAB中的23个字符:

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

答案 8 :(得分:10)

语言:C,字数:71

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

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

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

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

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

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

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

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

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

不适用于数字计数&gt; 6,虽然。

答案 9 :(得分:10)

<强>的Haskell

我把它归结为34个字符:

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

此表达式在评估时产生3.141596416935556。

编辑:这是一个使用foldl1而不是foldl的更短版本(33个字符):

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

编辑2:9 ^ 6而不是10 ^ 6。一个必须是经济的;)

编辑3:分别用foldl和foldl1替换foldl'和foldl1' - 由于编辑2,它不再溢出。感谢ShreevatsaR注意到这一点。

答案 10 :(得分:9)

<强> F#

尝试#1:

let pi = 3.14159

作弊?不,它以风格赢得胜利!

尝试#2:


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#。

答案 11 :(得分:7)

常见的lisp,55个字符。

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

答案 12 :(得分:6)

Mathematica,27个字符(可以说低至26,或高达33)

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

如果删除初始“N”,则它将答案作为(巨大的)分数返回。

如果作弊表示Mathematica不需要打印语句来输出结果,那么前面加上“Print@”总共33个字符。

NB:

如果硬编码术语的数量是作弊的,那么我认为没有任何答案能够做到这一点。检查当前术语何时低于某个阈值并不比硬编码术语数更好。仅仅因为当前术语仅改变第6或第7位并不意味着足够的后续术语总和不会改变第5位数。

答案 13 :(得分:5)

在交替序列中使用误差项的公式(因此,为实现所需精度而必要的迭代次数并未硬编码到程序中):

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;
}

答案 14 :(得分:4)

Perl:

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

总共42个字符。

答案 15 :(得分:4)

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

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

或者这个稍微长一点的非irb版本:

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

这是经过修改的莱布尼兹:

  1. 合并成对术语。这给你2/3 + 2/35 + 2/99 + ......
  2. Pi变为8 *(1 /(1 * 3)+ 1 /(5 * 7)+ 1 /(9 * 11)+ ...)

答案 16 :(得分:4)

C#:

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;
}

答案 17 :(得分:4)

F#(互动模式)(59个字符)

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

(收到警告但省略演员表)

答案 18 :(得分:3)

这是MUMPS中的解决方案。

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)

一些实证检验表明,当截断为5个小数点时,N = 272241是给出正确值3.14159的最低值。您必须转到N = 852365才能得到一个舍入到3.14159的值。

答案 19 :(得分:3)

对于记录,此Scheme实现有95个字符,忽略不必要的空格。

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

答案 20 :(得分:3)

使用迭代器块的C#:

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

答案 21 :(得分:3)

<强>使用Javascript:

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

在javascript中。 51个字符。显然不会赢,但是呃。 :P

编辑 - 感谢Strager,现在更新为46个字符。 :)


更新 (2010年3月30日)

更快(精确到5位小数)43个字符版本David Murdoch

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

答案 22 :(得分:2)

这是使用C#的递归答案。它只能在Release模式下使用x64 JIT,因为这是唯一应用尾部调用优化的JIT,并且由于系列收敛速度很慢,因此会导致StackOverflowException没有它。

IteratePi函数作为匿名lambda会很好,但由于它是自我递归的,我们必须开始用Y-combinators做各种可怕的事情,所以我把它留作了一个单独的功能。

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);
}

答案 23 :(得分:2)

语言:dc,字数:35

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

答案 24 :(得分:2)

目前大多数答案都假设它们在一定数量的迭代中会得到5位数的精确度,并且这个数字被硬编码到程序中。我对这个问题的理解是程序本身应该弄清楚它何时能得到一个精确到5位的答案并停在那里。根据这个假设,这是我的C#解决方案。我没有打算尽量减少角色的数量,因为它无法与已经存在的某些答案竞争,所以我想我会让它变得可读。 :)

    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;
        }
    }

答案 25 :(得分:2)

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

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

#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\n", p);
}

压缩版

#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\n",p);}

答案 26 :(得分:1)

红宝石:

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

答案 27 :(得分:1)

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\n", pi);

答案 28 :(得分:1)

注意到

之后
(= (- (/ 4 n)
      (/ 4 (+ n 2)))
   (/ 8 n (+ n 2)))

或者,用更熟悉的符号表示:

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

Common Lisp,带有do*循环(62个基本字符):

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

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

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

并使用扩展循环(86个基本字符):

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

所有这一切都假设初步检查我们必须走多远才能达到理想的准确度。

答案 29 :(得分:1)

C ++

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;
}

答案 30 :(得分:1)

#!/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)

答案 31 :(得分:1)

爪哇

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

答案 32 :(得分:1)

C#作弊 - 50个字符

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

它只是说“考虑到公式写一个函数......”它没有说以编程方式重现公式:)在盒子外思考......

C#LINQ - 78个字符

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

C#Alternate LINQ - 94 chars

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

最后 - 这需要前面提到的算法并以数学方式压缩它,这样你就不必担心不断变换符号了。

C#longhand - 89个字符(不计入不需要的空格)

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

答案 33 :(得分:1)

AWK中的64个字符:

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

答案 34 :(得分:0)

Lua,46个字符

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

答案 35 :(得分:0)

Python 3(40字节)

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

此版本使用@Svante's answer优化。

打印+7个字节

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

Python 2.x +1字节

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

打印+6个字节

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

http://codepad.org/amtxUxKp

答案 36 :(得分:0)

R - 27个字符

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

答案 37 :(得分:0)

PHP,99个字符

$output =

${!${''}=function(){$pi=1;for($i=0;$i<pow(10,6);$i++){$pi+=($i%2?1:-1)/(3+$i*2);}return $pi*4;}}();

var_dump($output);

我想有一些我不知道的漂亮技巧可以减少这个答案! 从这个post获得单行输出技巧。

答案 38 :(得分:0)

爪哇

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

答案 39 :(得分:0)

呃....作为数字处理的一般规则,应该将最小项中的系列与最大值相加,以避免精度损失的麻烦。所以在

FORTRAN77

剥离(248个字符)

      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个字符)

      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

输出:

   3.1415901

似乎适用于任意数字的数字,最高为6ish,其中real的精度用完了。 针对速度或最小操作数进行了优化。

答案 40 :(得分:0)

在关于有争议的意见的主题中阅读面试问题之后,我只是写了这篇文章。它不漂亮,但它花了我3-4分钟,我正在检查每个循环的准确性。 C ++。我明天会醒来并发布一个不太糟糕的解决方案:)

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;
  }



}

答案 41 :(得分:0)

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

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);
   }

答案 42 :(得分:0)

Erlang,~126个字符:

-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.

答案 43 :(得分:0)

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

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

仅限表达式:74个字符,没有不必要的空格。

答案 44 :(得分:0)

VB 117 chars

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字符(省略不必要的行继续)

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

然后致电:

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

答案 45 :(得分:-1)

1个字符: 。 写于“MySuperDuperDomainSpecificLanguageThatOnlyReturnsThisOneAnswerAndNothingElse”。

是的,这只是一个笑话,但严重的是,除非你不允许使用DSL,否则每个Code Golf竞赛都可以通过一些goober来赢得,他会用自己的语言写一个字符,只返回一个结果......