用二次收敛计算π


20

编写一个函数或完整的程序,该程序需要一个正数n并执行n迭代算法的步骤,以计算具有二次收敛性的π(即,每次迭代将其精确数字的数量大约加倍),然后返回或打印2 n个正确的数字(包括开始3)。Gauss-Legendre算法就是这样一种算法,但是如果您愿意,可以自由使用其他算法。

例子:

输入1→输出3.1
输入2→输出3.141
输入5→输出3.1415926535897932384626433832795

要求:

  • 该算法的每次迭代都必须执行恒定数量的基本运算,例如加,减,乘,除,乘方和根(具有整数指数/度)-对“大”整数/十进制数的每个此类运算都被计为偶数如果它在内部涉及一个或多个循环。需要明确的是,涉及复数的三角函数和幂不是基本运算。
  • 期望该算法具有初始化步骤,该初始化步骤还必须具有恒定数量的操作。
  • 如果算法需要再进行1或2次迭代才能获得2 n个正确的数字,则您最多可以执行n+2迭代,而不仅仅是执行n
  • 如果还不够清晰,请在正确的2 n位数字之后,程序不得打印其他任何内容(例如,更正确的数字,错误的数字或莎士比亚的完整著作)。
  • 您的程序必须支持n从1到至少20的值。
  • n在现代计算机上,= 20的程序花费的时间不应超过一个小时(这不是硬性规定,但是请尽量使其合理)。
  • 在算法的初始化和第一次迭代之后,程序不得获得超过20个准确的数字。
  • 该程序必须可使用免费软件在Linux中运行。
  • 源代码只能使用ASCII字符。

得分:

简单明了的代码高尔夫,最短的代码获胜。

优胜者:

赢家是Digital Trauma,我终于在n = 20上完成了他的代码(只是在开玩笑)。Primo因其非常快速的python解决方案和不同的算法而获得了特别奖:)


1
二次收敛误差〜N ^(1/2)。您描述的是指数收敛误差〜2 ^(-N)
2015年

@yo'是说维基百科是错误的吗?
aditsu

1
至少可以说是一种误导:“二次收敛”是~q^(n^2)根据那里的第一部分和那里~q^2的第二部分。
2015年

1
我不懂代码高尔夫:肯定有人可以专门为这样的单个任务编写自己的编程语言,然后编写一个0字节的程序吗?
theonlygusti 2015年

2
@theonlygusti,这将是一个标准漏洞,将被取消参赛资格
–aditsu

Answers:


14

dc,99位元组

打高尔夫球:

2?dsi1+^k1dddsa2v/sb4/stsp[lalb*vlalb+2/dla-d*lp*ltr-stsasblp2*spli1-dsi0<m]dsmxK2/1-klalb+d*4lt*/p

带有空格和“可读性”的注释:

2?dsi               # Push 2. push input n, duplicate and store in i
1+^k                # Set calculation precision to 2^(n+1)
1dddsa              # Push four 1s. Store 1st in a
2v/sb               # Store 1/sqrt(2) in b
4/st                # Store 1/4 in t
sp                  # Store 1 in p
[                   # Start iteration loop macro
lalb*v              # Save sqrt(a*b) on stack
lalb+2/d            # Save a[i+1] = (a[i]+b[i])/2 on stack and duplicate
la-d*lp*ltr-        # Save t-p(a[i]-a[i+1])^2 on the stack
st                  # Store t result from stack
sa                  # Store a result from stack
sb                  # Store b result from stack
lp2*sp              # Store 2p in p
li1-dsi0<m]         # Decrement iteration counter i; recurse into macro if < 0
dsmx                # Duplicate, store and run macro
K2/1-k              # Set display precision to 2^n-1
lalb+d*4lt*/        # Save (a+b)^2/4t on stack
p                   # Print result

dc需要告知应使用多少位数的精度。计算精度需要高于最终显示精度,因此将计算精度设置为2^(n+1)数字。我已经针对http://www.angio.net/pi/digits/pi1000000.txt验证了n = 10的输出的准确性。

对于较大的n,该速度显着降低。n = 12在我的VM上需要1.5分钟。运行几个不同的样本显示时间复杂度为O(e ^ n)(不足为奇)。将其推断为n = 20将需要233天的运行时间。那好吧。至少比宇宙热死更好。

这是中等程度的练习-堆栈用于在每次迭代的计算过程中消除临时变量,但是可能会更多地使用堆栈来缩短此时间。

$ dc glpi.dc <<< 1
3.1
$ dc glpi.dc <<< 2
3.141
$ dc glpi.dc <<< 5
3.1415926535897932384626433832795
$ time dc glpi.dc <<< 7
3.1415926535897932384626433832795028841971693993751058209749445923078\
164062862089986280348253421170679821480865132823066470938446

real    0m0.048s
user    0m0.039s
sys 0m0.000s
$ 

如果您不喜欢dc以70个字符包装输出,可以将环境变量设置DC_LINE_LENGTH为0:

$ DC_LINE_LENGTH=0 dc glpi.dc <<< 8
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648
$ 

2
哈哈,“可读性”。并不是真的适用于DC。;)
Alex A.

输入5似乎打印了超过32位数字
aditsu

我为此添加了一条规则,再加上一条关于运行时间的规则(但并不十分严格)。我也不喜欢您的输出如何以反斜杠分割成多行,这是否是dc的限制?
aditsu

恐怕n = 6的输出是错误的
aditsu

1
太好了,您也将它控制在100以下:)您还可以发布没有空格/注释的实际高尔夫99字符程序吗?
aditsu

10

R,156个字节

让我们开始这个聚会吧……从高斯勒格朗德算法的绝对天真实现开始。

for(i in 1:scan()){if(i<2){a=p=Rmpfr::mpfr(1,2e6);t=a/4;b=t^t}else{x=(a+b)/2;b=(a*b)^.5;t=t-p*(a-x)^2;a=x;p=2*p};o=(a+b)^2/(4*t)};cat(Rmpfr::format(o,2^i))

取消+说明:

# Generate n approximations of pi, where n is read from stdin
for (i in 1:scan()) {

    # Initial values on the first iteration
    if (i < 2) {
        a <- p <- Rmpfr::mpfr(1, 1e7)
        t <- a/4
        b <- t^t
    } else {
        # Compute new values
        x <- (a + b) / 2
        b <- (a*b)^0.5
        t <- t - p*(a - x)^2

        # Store values for next iteration
        a <- x
        p <- 2*p
    }

    # Approximate pi 
    o <- (a + b)^2 / (4*t)
}

# Print the result with 2^n digits
cat(Rmpfr::format(o, 2^i))

mpfr()功能是Rmpfr程序包的一部分。它mpfr使用第一个参数作为值,第二个参数作为精度的位数创建对象。我们将a和赋给p1,并通过t基于a(和b基于t)定义,该mpfr类型传播到所有四个变量,从而始终保持精度。

如前所述,这需要R包Rmpfr,它是“ R Multiple Precision Floating Point Reliable”的首字母缩写。该软件包在后台使用GMP。不幸的是,基数R不具有执行高精度算术的能力,因此没有封装依赖性。

没有Rmpfr吗?没汗 install.packages("Rmpfr")您的所有梦想都会实现。

请注意,已将2e6其指定为精度。这意味着我们有2,000,000位精度,足以至少保持n= 20的精度。(注意:n= 20需要很长时间,但在我的计算机上不到一个小时。)

实际上,这里的方法只是对Wikipedia页面上的公式的反驳,但是,嘿,我们必须从某个地方开始。

一如既往的欢迎任何输入!


我不得不重写很多内容,但我仍然不得不承认,彼得·泰勒(Peter Taylor)帮助我将第一得分降低了70个字节。用DigitalTrauma的话来说,就是“繁荣”。


7

Python 2,214字节

这个挑战为我学习十进制模块提供了一个很好的借口。十进制数具有确定的精度并具有平方根支持。我已根据循环数将精度设置为对精度的保守估计。

更新资料

我已经更新了该程序,以提高准确性和速度,但以打高尔夫球为代价。通过使用十进制sqrt()方法并将x**2用法替换为x*x,它现在快了200倍。这意味着它现在可以计算20个循环,从而在6.5小时内给出一百万位数的结果。十进制数的最后一位经常有错误(由于精度限制而导致的操作),因此程序现在使用并丢弃5个额外的位,因此仅打印准确的位。

from decimal import*
d=Decimal
e=input()
getcontext().prec=5+(1<<e)
k=d(1)
j=d(2)
g=j*j
h=k/j
a=k
b=k/j.sqrt()
t=k/g
p=k
for i in[0]*e:f=a;a,b=(a+b)/j,(a*b).sqrt();c=f-a;t-=c*c*p;p+=p
l=a+b
print str(l*l/g/t)[:-5]

样品运行:

$ echo 1 | python min.py 
3.1
$ echo 2 | python min.py 
3.141
$ echo 3 | python min.py 
3.1415926
$ echo 5 | python min.py 
3.1415926535897932384626433832795
$ echo 12 | python min.py
3.141592653589793238462643383279502884197169399375105820974944592307816406286208
99862803482534211706798214808651328230664709384460955058223172535940812848111745
02841027019385211055596446229489549303819644288109756659334461284756482337867831
65271201909145648566923460348610454326648213393607260249141273724587006606315588
17488152092096282925409171536436789259036001133053054882046652138414695194151160
94330572703657595919530921861173819326117931051185480744623799627495673518857527
24891227938183011949129833673362440656643086021394946395224737190702179860943702
77053921717629317675238467481846766940513200056812714526356082778577134275778960
91736371787214684409012249534301465495853710507922796892589235420199561121290219
60864034418159813629774771309960518707211349999998372978049951059731732816096318
59502445945534690830264252230825334468503526193118817101000313783875288658753320
83814206171776691473035982534904287554687311595628638823537875937519577818577805
32171226806613001927876611195909216420198938095257201065485863278865936153381827
96823030195203530185296899577362259941389124972177528347913151557485724245415069
59508295331168617278558890750983817546374649393192550604009277016711390098488240
12858361603563707660104710181942955596198946767837449448255379774726847104047534
64620804668425906949129331367702898915210475216205696602405803815019351125338243
00355876402474964732639141992726042699227967823547816360093417216412199245863150
30286182974555706749838505494588586926995690927210797509302955321165344987202755
96023648066549911988183479775356636980742654252786255181841757467289097777279380
00816470600161452491921732172147723501414419735685481613611573525521334757418494
68438523323907394143334547762416862518983569485562099219222184272550254256887671
79049460165346680498862723279178608578438382796797668145410095388378636095068006
42251252051173929848960841284886269456042419652850222106611863067442786220391949
45047123713786960956364371917287467764657573962413890865832645995813390478027590
09946576407895126946839835259570982582262052248940772671947826848260147699090264
01363944374553050682034962524517493996514314298091906592509372216964615157098583
87410597885959772975498930161753928468138268683868942774155991855925245953959431
04997252468084598727364469584865383673622262609912460805124388439045124413654976
27807977156914359977001296160894416948685558484063534220722258284886481584560285
06016842739452267467678895252138522549954666727823986456596116354886230577456498
03559363456817432411251507606947945109659609402522887971089314566913686722874894
05601015033086179286809208747609178249385890097149096759852613655497818931297848
21682998948722658804857564014270477555132379641451523746234364542858444795265867
82105114135473573952311342716610213596953623144295248493718711014576540359027993
44037420073105785390621983874478084784896833214457138687519435064302184531910484
81005370614680674919278191197939952061419663428754440643745123718192179998391015
91956181467514269123974894090718649423196156794520809514655022523160388193014209
37621378559566389377870830390697920773467221825625996615014215030680384477345492
02605414665925201497442850732518666002132434088190710486331734649651453905796268
56100550810665879699816357473638405257145910289706414011097120628043903975951567
71577004203378699360072305587631763594218731251471205329281918261861258673215791
98414848829164470609575270695722091756711672291098169091528017350671274858322287
18352093539657251210835791513698820914442100675103346711031412671113699086585163
98315019701651511685171437657618351556508849099898599823873455283316355076479185
35893226185489632132933089857064204675259070915481416549859461637180270981994309
92448895757128289059232332609729971208443357326548938239119325974636673058360414
28138830320382490375898524374417029132765618093773444030707469211201913020330380
19762110110044929321516084244485963766983895228684783123552658213144957685726243
34418930396864262434107732269780280731891544110104468232527162010526522721116603
96665573092547110557853763466820653109896526918620564769312570586356620185581007
29360659876486117

取消编码:

from decimal import *
d = Decimal

loops = input()
# this is a conservative estimate for precision increase with each loop:
getcontext().prec = 5 + (1<<loops)

# constants:
one = d(1)
two = d(2)
four = two*two
half = one/two

# initialise:
a = one
b = one / two.sqrt()
t = one / four
p = one

for i in [0]*loops :
    temp = a;
    a, b = (a+b)/two, (a*b).sqrt();
    pterm = temp-a;
    t -= pterm*pterm * p;
    p += p

ab = a+b
print str(ab*ab / four / t)[:-5]

4
half = one/two
-Digital Trauma

看来您没有打印正确的数字位数。而且我想知道速度慢是否是由于不必要的使用**
aditsu

1
@aditsu,我已将精度降低到预期的位数(但是从一次迭代中放弃非常好的精度会使我的牙齿发痒)。关于**效果的好建议。通过摆脱它们,我发现了很多速度。但是我无法在1小时内遇到20个循环。也许是pypy或Cython?嗯 我会考虑的。
逻辑骑士

更好的方法是:)对于这个问题,放弃良好的准确性比继续陷入不良的准确性要少。1小时的限制是基于我在Java 8上运行的cjam / java测试代码的。也许python没有对大数有效的乘法/除法/ sqrt(Karatsuba&co)?
aditsu

@aditsu:我相信整数具有karatsuba(仅此而已)-但具有32位肢体大小而不是64位肢体大小。谁知道十进制。

5

Python(2.7)-131字节

from gmpy import*
n=input()
p=a=fsqrt(mpf(8,4<<n));b=0
exec"a=fsqrt(a/2);b=1/(a-a*b+b/a+1);p*=b+a*a*b;a+=1/a;"*n
print`p`[5:2**n+6]

更新:现在使用gmpy而不是gmpy2。不管出于什么原因,在gmpy2单个值上设置精度不会传播到其他值。任何计算的结果都将还原为当前上下文的精度。精度确实会在中传播gmpy,这对我来说似乎更直观。它的冗长程度也大大降低。

使用由Borwein和Borwein设计的众多算法之一,对其进行了稍微的重构。n = 20在我的盒子上大约需要11秒。不是最有效的方法,但仍然不错。


重构

原始算法如下:




重构是逐步完成的,但最终结果是




主要的简化发生在p n + 1。由于消除了除法,因此速度也稍快。

当前的实现将p n + 1的计算中n个迭代推后了一次,这允许对p 02√2)进行不同的初始化,但是在其他方面相同。


样品用量

$ echo 1 | python pi-borwein.py
3.1

$ echo 2 | python pi-borwein.py
3.141

$ echo 5 | python pi-borwein.py
3.1415926535897932384626433832795

$ echo 10 | python pi-borwein.py
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278

太好了,但是您缺少n = 7的数字
aditsu

另外,这是算法吗?
aiditsu

@aditsu已修复,是的。
primo

现在,对于n = 5,最后一位数字是错误的
aditsu

1
@aditsu pip install gmpy为我工作;gmpy并且gmpy2是单独的软件包。但是,它确实依赖于已弃用的distutils
2015年

3

bc和牛顿方法,43个字节

牛顿求任何函数零点的方法二次收敛,并且算法比高斯-勒根德勒算法简单得多。基本上可以归结为:

xnew = xold-f(xold)/ f'(xold)

因此,下面是相应的代码段:

n=20;x=3;scale=2^n;while(n--)x-=s(x)/c(x);x

更具可读性:

/* desired number of iterations */
n = 20;

/* starting estimate for pi */
x = 3;

/* set precision to 2^n */
scale = 2^n;

/* perform n iteration steps */
while(n--)
  // f:=sin, f'=cos
  x -= s(x)/c(x)

要对此进行测试,请bc -l在外壳中运行并粘贴以上代码段。准备等待一段时间;n=20现在已经运行了大约5分钟,而且还看不到尽头。n=10大约需要40秒。


4
不确定正弦和余弦是否符合“基本运算,例如加法,减法,乘法,除法和幂(包括根)”的要求。但是,如果您滚动自己的正弦/余弦,那将是可以接受的。
2015年

1
简洁的公式-它说π是f(x)= x-tan(x)的不动点
Casey Chu
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.