计算pi到5位小数


15

这来自http://programmers.blogoverflow.com/2012/08/20-controversial-programming-opinions/

“鉴于可以使用函数4 *(1 – 1/3 + 1/5 – 1/7 +…)来估计Pi,更多的项可以提供更高的精度,因此编写一个函数可以计算出Pi的精度为小数点后5位。 ”

  • 注意,必须通过计算上面给出的顺序来完成估算。

8
您可能应该添加更多规则,否则您将获得类似(python)的答案p=lambda:3.14159
Matt

1
您是否看到过codegolf.stackexchange.com/questions/506/…,这非常相似?至少,应禁止使用trig函数解决此问题,因为它们允许使用诸如QBASIC程序之类的简单解决方案:?INT(4E5 * ATN(1))/ 1E5
PleaseStand

我认为您应该要求算法是逐次逼近算法之一:计算时间越长,就越接近pi。
DavidC 2012年

@DavidCarraher,尽管从数学上讲这是不可避免的,但从数值分析的角度来看,这是非常可疑的。缓慢收敛的交替序列是失去意义的典型代表。
彼得·泰勒

2
Dupe,但它太旧了,不在这里:stackoverflow.com/q/407518/12274
JB

Answers:


10

JavaScript,46 58 56 45字节

ES6更新:事实证明,五年过去了,我们还有更多可用的功能。

let f=(i=0,a=0)=>i>1e6?a:f(i+4,a+8/-~i/(i+3))

理论上,此版本(45字节;是,let必需)在ES6严格模式下工作。实际上,您可以使用V在V8中运行它(例如,带节点)--use-strict --harmony-tailcalls。a,适当的语音提示功能尚未广泛实施。但是,它是特定的行为,所以应该没问题。

如果我们要坚持广泛实现的内容,并且不需要严格模式,则可以简单地对功能使用ES6胖箭头语法,但可以保留48字节的成本,与以前相同(由Brian H建议)。

a=>{for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}

名称为单参数的选择不确实的事,但我们不妨挑我们使用,以减少全球范围的污染的名称之一。


function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}

这个版本是一个函数表达式;f如果要命名,请添加两个字符(例如“ ”)。这个版本破坏了全局ai; 如果我们a,i在参数列表中添加“ ”,则可以避免这种情况。

为了避免减法,使用了算法的重新编制版本。

 1/1 - 1/3  +   1/5 - 1/7   +    1/9 - 1/11  + ...
(3/3 - 1/3) + (7/35 - 5/35) + (11/99 - 9/99) + ...
    2/3     +      2/35     +       2/99     + ...
  2/(1*3)   +    2/(5*7)    +     2/(9*11)   + ...

这是未经调整的“普通”版本:

function(){for(a=0,i=1;i<1e6;i+=2)a+=[,4,,-4][i%4]/i;return a}

时钟为64 62个字符。

感谢@ardnew提出的摆脱4*之前的建议return


历史

function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}     // got rid of `i+=4`; restructured
// Old versions below.
function(){for(a=0,i=1;i<1e6;i+=4)a+=8/i/-~-~i;return a}    // got rid of `4*`
function(){for(a=0,i=1;i<1e6;i+=4)a+=2/i/-~-~i;return 4*a}

very非常好,减去了减法。
acolyte

1
伟大的工作,但需要编写为适当的功能
ardnew

@ardnew:谢谢,当我阅读问题描述时,我一定错过了那个细节。我已经对其进行了更新,现在它是一个可调用的函数表达式(lambda);不知道是否允许使用此名称,或者是否必须为其指定名称。如果是这样,无论如何也只是另外两个字符。
FireFly 2012年

1
@FireFly您还可以通过更改剃掉2个字符a+=2/i/-~-~i;return 4*aa+=8/i/-~-~i;return a
ardnew

@ardnew:太棒了!没想到。:D
FireFly 2012年

8

Python 59字节

print reduce(lambda x,p:p/2*x/p+2*10**999,range(6637,1,-2))

打印出1000位数;略大于所需的5。它不使用规定的迭代,而是使用以下代码:

pi = 2 + 1/3*(2 + 2/5*(2 + 3/7*(2 + 4/9*(2 + 5/11*(2 + ...)))))

6637(最里面的分母)可配制成:

数字* 2 *日志2(10)

这意味着线性收敛。每次更深的迭代将产生pi的另一个二进制位。

但是,如果您坚持使用 tan -1身份,那么即使您不介意解决该问题也可以实现类似的收敛。看一下部分和:

4.0,2.66667,3.46667,2.89524,3.33968,2.97605,3.28374,...

显然,每一项都来回跳到收敛点的任一侧;该系列具有交替收敛。此外,每一项比上一项更接近收敛点;就其收敛点而言,它绝对是单调的。这两个属性的组合暗示着,任何两个相邻项的算术平均值均比任一项本身更接近收敛点。为了让您更好地理解我的意思,请考虑以下图像:

Partial Sums

外部序列是原始序列,内部序列是通过取每个相邻项的平均值来找到的。明显的不同。但是真正引人注目的是,这个新系列还具有交替收敛性,并且就其收敛点而言绝对是单调的。这意味着该过程可以一遍又一遍地应用于恶心。

好。但是如何?

一些正式的定义。令P 1(n)一个序列的第n 项,P 2(n)为第二个序列的 n 项,类似地,P k(n) k 序列的 n 项,如上所述。

P 1 = [P 1(1),P 1(2),P 1(3),P 1(4),P 1(5),...]

P 2 = [(P 1(1)+ P 1(2))/ 2,(P 1(2)+ P 1(3))/ 2,(P 1(3)+ P 1(4))/ 2,(P 1(4)+ P 1(5))/ 2,...]

P 3 = [(P 1(1)+ 2P 1(2)+ P 1(3))/ 4,(P 1(2)+ 2P 1(3)+ P 1(4))/ 4,(P 1(3)+ 2P 1(4)+ P 1(5))/ 4,...]

P 4 = [(P 1(1)+ 3P 1(2)+ 3P 1(3)+ P 1(4))/ 8,(P 1(2)+ 3P 1(3)+ 3P 1(4) + P 1(5))/ 8,...]

毫不奇怪,这些系数正好遵循二项式系数,并且可以表示为Pascal三角形的单行。由于任意一列Pascal三角形的计算都是微不足道的,因此只需取前n个部分和,将每个n和乘以Pascal三角形的 k行中的相应项,然后除以2,就可以找到任意“深”系列。K-1

这样,仅需36次迭代就可以实现完整的32位浮点精度(〜14个小数位),此时部分和甚至没有收敛到第二个小数位。这显然不是打高尔夫球的:

# used for pascal's triangle
t = 36; v = 1.0/(1<<t-1); e = 1
# used for the partial sums of pi
p = 4; d = 3; s = -4.0

x = 0
while t:
  t -= 1
  p += s/d; d += 2; s *= -1
  x += p*v
  v = v*t/e; e += 1

print "%.14f"%x

如果您想要任意精度,只需稍作修改即可实现。在这里再次计算1000位数字:

# used for pascal's triangle
f = t = 3318; v = 1; e = 1
# used for the partial sums of pi
p = 4096*10**999; d = 3; s = -p

x = 0
while t:
  t -= 1
  p += s/d; d += 2; s *= -1
  x += p*v
  v = v*t/e; e += 1

print x>>f+9

p的初始值开始大2 10,以抵消s / d的整数除法效应,因为d变大,导致最后几位数字不收敛。再次注意这里3318也是:

数字*日志2(10)

迭代次数与第一种算法相同(减半,因为t每次迭代减少1而不是2)。再次表明这是线性收敛:每次迭代pi的一个二进制位。在这两种情况下,都需要进行3318次迭代才能计算pi的 1000位数,这比100万次迭代计算5的配额更好。


这比我的解决方案要好得多:4 * sum(1/(1+i*2) if not i%2 else -1/(1+i*2) for i in xrange(places*10**(places)))
亚伦·霍尔

1
这与我的方法非常相似,碰巧是您的另一种形式。在我的中,随着k → ∞f(-1,k)您接近Euler-sum。
Simply Beautiful Art

1
很酷; 很棒的分析和解释,谢谢。
杰里米·拉德克里夫

只是一件小事。您不是在意思之后P_1 = ..., P_2 = ..., P_3 = ..., P_4 = ...,“ ...将每个数乘以kthPascal三角形行中的相应项,然后除以2^{k-1}。”,而不是nth行和2^{n-1}?。
杰里米·拉德克里夫

@jeremyradcliff我做到了,是的。感谢您的更正。
primo

5

Mathematica 42 39 34 33 31 26 32

阿基米德的方法 26个字符

N@#*Sin[180 Degree/#]&

当输入为822时,达到标准。

问题:有人知道他是如何计算180度正弦的吗?我不。


莱布尼兹的方法(格雷戈里的系列)32个字符

这是问题构成者提供的示例相同的功能。它在大约一半的迭代中达到了标准。

N@4Sum[(-1)^k/(2k+1),{k,0,10^6}]

Madhava-Leibniz方法 37个字符

这个变体使用了更多的字符,但仅9次迭代就收敛到标准!

N@Sqrt@12 Sum[(-1/3)^k/(2k+1),{k,0,9}]

那些都是通过问题定义中给出的算法来计算的?
acolyte 2012年

@acolyte Leibniz的方法(现在列出了第一个)确实是问题描述中提到的方法。收敛非常慢。它的微小变化(Madhava-Leibniz)收敛很快。
DavidC 2012年

180°正弦非常容易。这是180°/ N可以得到N的秋后算账的麻烦之外
JB

请解释,@ JB整rick要衡量吗?
DavidC

该条目应声明为“ 32”,因为只有莱布尼兹的方法可以满足要求(按给定的代码计算字符,我得到34,但是两个空格都可以安全删除,长度确实为32)。
celtschk


4

Java(67个字符)

float r(){float p=0,s=4,i=1E6f;while(--i>0)p+=(s=-s)/i--;return p;}

请注意,这可以通过按正确的顺序将数字相加来避免重要性下降。


这也是完全兼容的C代码。如果发布为C,则可以更改while(--i>0)while(i--)并保存2个字符
ardnew

1
@ardnew,是的,但是使用C,还有更多有趣的玩法...
Peter Taylor

4

Haskell,32岁

foldr(\k->(4/(2*k+1)-))0[0..8^7]

GHCi>文件夹(\ k->(4 /(2 * k + 1)-))0 [0..8 ^ 7]
3.141593130426724

算一个函数名

34

π=foldr(\k->(4/(2*k+1)-))0[0..8^7]


3

C(GCC)(44个字符)

float p(i){return i<1E6?4./++i-p(++i):0;}

那是41个字符,但是还必须进行编译-O2才能获得优化器,以消除尾部递归。这也依赖于++执行顺序的不确定行为。感谢ugoren指出这一点。我已经在64位Linux下使用gcc 4.4.3进行了测试。

请注意,除非优化程序也对总和进行重新排序,否则它将从最小的数开始进行加法运算,因此避免了重要性的损失。

呼叫为p()


您的递归调用q()不是p()。而且我不认为-O2应该算(但是如果您算了算,由于需要的空间,它是4个字符)。
ugoren 2012年

另外:1. gcc 4.1.1不能优化递归(我不知道如何做到),所以堆栈溢出。2.应该称为p(0)。3.保存一个字符return++i...。4.两个++i使行为不确定。
ugoren

@ugoren,感谢您的评论。为了:q-这将教会我重命名后再次检查。我认为我按照正常的惯例-O2将3个字符计数,但是如果需要,我们可以在metas上打开它。meta.codegolf.stackexchange.com/questions/19是我能找到的唯一相关讨论。我添加了我正在使用的gcc版本,该版本允许我将其称为p()。保存字符将停止优化程序并给出段错误。根据meta.codegolf.stackexchange.com/questions/21
Peter Taylor

我为有关标志的元问题添加了答案。关于p()-您确定p()在任何情况下都可以拨打电话吗?还是仅仅是测试堆栈中发生了什么?
ugoren

@ugoren,也许我一直很幸运。即使我连续两次调用它,第二个仍然返回正确的值。gcc似乎为p()vs 生成了稍微不同的代码p(0),但是我不知道它记录了什么行为,而且我不是C程序员。
彼得·泰勒

3

J,26个字符

+ / + / _ 2((4 _4)&%)>:+:i.100

从100个序列项目移至1e6个项目。现在它也是一个代码标记,可以从浏览器复制粘贴到控制台而不会出现错误。

+/+/_2((4 _4)&%)\>:+:i.1e6

3
-/4%>:2*i.1e6-13个字符。(由于#jsoftware中的b_jonas使我意识到-/可以计算具有交替符号的和。[这是因为J中的所有运算符都具有相同的优先级并且是右关联的,所以-/ 1 2 3 4<=> 1 - (2 - (3 - 4))<=> 1 - 2 + 3 - 4。))
FireFly

那很整洁,是真棒的两倍。甚至更棒2 ^ 10!
fftw 2012年

@FireFly真漂亮
约拿(Jonah)

2

Javascript-33个字符

p=x=>4*(1-(x&2))/x+(x>1?p(x-2):0)

调用p传递正的奇数x,它将计算带(x-1)/2项的Pi 。


2

红宝石-82个字符

def f(n,k=n)k>0?(f(n,k-1)+f(n+1,k-1))/2:n<0?0:f(n-1,0)+(-1)**n/(2*n+1.0)end;4*f(9)

试试看:https : //repl.it/LQ8w

该方法通过数值加速方法间接使用给定的序列。结果输出是

pi ≈ 3.14159265161

pi = 3.14159265359

它开始于

f(n,0) = 1/1 - 1/3 + 1/5 - ... + ((-1)**n)/(2*n+1)

然后,由于这是交替的,我们可以使用

f(n,1) = (f(n,0) + f(n+1,0))/2

并反复应用:

f(n,k) = (f(n,k-1) + f(n+1,k-1))/2

为简单起见,f(n) = f(n,n)


红宝石-50个字符

如果您不介意长时间运行,则可以使用

def f(n)n<0?0:f(n-1)+(-1)**n/(2*n+1.0)end;4*f(1e7)

要么

a=0;for k in 0..1e7 do a+=(-1)**k/(2*k+1.0)end;4*a

1

C,69个字符

float p,b;void main(a){b++<9e6?p+=a/b++,main(-a):printf("%f\n",4*p);}
  • 在没有命令行参数的情况下运行(因此 a被初始化为1)。
  • 必须进行优化编译。
  • void main是奇怪且非标准的,但可以使事情正常。没有它,递归将被实现为真正的调用,从而导致堆栈溢出。另一种方法是添加return
  • 4*如果使用三个命令行参数运行,则可以保存两个字符。

您可以将其缩短为int main(a)甚至main(a)GCC仅发出警告。void main无论如何,它都会发出警告,甚至可能是因为您只有一个参数main
nyuszika7h 2014年

1

Clojure-79个字符

(fn [](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))

这将创建一个没有参数的函数,该函数将计算一个将pi正确近似为五个小数位的浮点数。请注意,这不会将函数绑定到诸如的名称pi,因此,必须使用evalas 来对代码进行评估,(<code>)或者将解决方案绑定至名称,在这种情况下,解决方案是

(defn p[](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))

为82个字符

关于

(defn nth-term-of-pi [n] (* (Math/pow -1 n) (/ 1.0 (+ 1 n n))))
(defn pi [c] (* 4 (apply + (map nth-term-of-pi (range c)))))
(def  pi-accuracy-constant (loop [c 1000] (if (< (pi c) 3.14159) (recur (inc c)) c)))
; (pi pi-accuracy-constant) is then the value of pi to the accuracy of five decimal places

1

PHP- 56 55个字符

<?for($j=$i=-1;1e6>$j;){$p+=($i=-$i)/($j+=2);}echo$p*4;

我不知道在不违反算法规则的情况下可以缩小尺寸。


1
45岁呢?<?for(;1e6>$j;)$p+=($i=-$i|4)/~-$j+=2;echo$p;
primo 2012年

我试图解决这个问题,但是无法按位操作。谢谢你的建议!
TwoScoopsofPig 2012年

您可以删除最后一个分号以保存1个字符。
nyuszika7h 2014年

1

Perl- 43 39个字符

不确定匿名子例程的规则,但这是使用@FireFly的系列构造的另一种实现

sub{$s+=8/((4*$_+2)**2-1)for 0..1e6;$s}

sub p{$s+=(-1)**$_*4/(2*$_+1)for 0..1e6;$s}


0

Java- 92 84个字符

我无法超越Peter Taylor的结果,但这是我的:

double d(){float n=0,k=0,x;while(n<9E5){x=1/(1+2*n++);k+=(n%2==0)?-x:x;}return 4*k;}

非高尔夫版本:

double d() {
    float n = 0, k = 0, x;
    while (n < 9E5) {
        x = 1 / (1 + 2 * n++);
        k += (n % 2 == 0) ? -x : x;
    }
    return 4 * k;
}

编辑:使用三元运算符保存了一些字符。


0

Python-56个字符

嗯,我的python-fu不够强大。我再也看不到捷径了,但是也许经验丰富的高尔夫球手可以在这里找到一些合适的东西?

t=s=0
k=i=1
while t<1e6:t,s,i,k=t+1,k*4./i+s,i+2,-k

您可以使用Python 3为浮点除法(4.-> 4)保存一个字节。在其他新闻中,我刚刚发现一个案例,其中Python 3在代码高尔夫方面实际上胜过Python 2!
nyuszika7h 2014年

0

红宝石-54个字符

def a()p=0;1000000.times{|i|p+=8/(4*i*(4*i+2))};p;end;

我第一次尝试在控制台上

def a()i=1;p=0;while i<2**100 do p+=8/(i*(i+2));i+=4;end;p;end;

63个字符。


您可以使用def a;代替来保存一个字节def a()
nyuszika7h 2014年

另一个删除最后一个分号。
nyuszika7h 2014年

0

Perl(76个字符)

$y=1e4;for$x(0..1e4-1){$y--while sqrt($x**2+$y**2)>1e4;$a+=$y}print 4*$a/1e8

(结果:3.14159052)

不是最短的解决方案,但也许很有趣。这是一个几何图形。我计算一个圆圈下的面积。

我有另一种有趣的方法,但是它确实很慢。它计算四分之一圆以下的正方形中离散点的数量,并从中计算出pi:

$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2

它期望将迭代次数作为命令行参数。在这里,您可以看到运行时间与准确性的关系。;)

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 100
3.1796
real    0m0.011s
user    0m0.005s
sys 0m0.003s

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 1000
3.14552
real    0m0.354s
user    0m0.340s
sys 0m0.004s

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 10000
3.14199016
real    0m34.941s
user    0m33.757s
sys 0m0.097s

0

k(25个字符)

4 * + /%(i#1 -1)'1 + 2!i:1000000

稍短:

+/(i#4 -4)%1+2*!i:1000000

0

巨蟒(49)

print 4*sum((-1)**i/(2*i+1.)for i in range(9**6))
3.14159 453527



0

SQL,253个字节

DECLARE @B int=3, @A varchar(max), @C varchar(max)='1'
WHILE @B<100000
BEGIN
SELECT @C=@C+(select case when (@B-1)%4=0 then'+'else'-'end)+
(SELECT cast(cast(1.0/@B as decimal(9,8)) as varchar(max)))
SELECT @B=@B+2
END
EXECUTE('SELECT 4*('+@C+')')

我会提供一个SQL Fiddle,但这会导致很多循环深入查找1/3 1/5 1/7等分数,并给出错误大声笑。但是,如果更改@B<100000为,1000那么它将运行(显然精度不相同)。


0

Befunge,129个字节

p08p109p^v*86%+55:<$$$<
\$\>:#,_@>+\55+/:#^_"."
v>p"~"/:"~"%08p"~"/00p:2\4%-*"(}"
8^90%"~":+2:+g90*+g80*<
>*:**\/+>"~~"00g:"~"`!|

在线尝试!

如果有人想知道,那是一头大象。

Cartoon elephant

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.