Tính số π với độ hội tụ bậc hai


20

Viết hàm hoặc chương trình hoàn chỉnh lấy số dương nvà thực hiện ncác bước của thuật toán lặp để tính số π có hội tụ bậc hai (nghĩa là nó nhân đôi số chữ số chính xác ở mỗi lần lặp) sau đó trả về hoặc in ra 2 n chữ số chính xác (bao gồm cả đầu 3). Một thuật toán như vậy là thuật toán Gaussâu Legendre , nhưng bạn có thể tự do sử dụng một thuật toán khác nếu bạn thích.

Ví dụ:

đầu vào 1→ đầu 3.1
vào đầu vào 2→ đầu 3.141
vào đầu 5ra → đầu ra3.1415926535897932384626433832795

Yêu cầu:

  • Mỗi lần lặp của thuật toán phải thực hiện một số lượng lớn các hoạt động cơ bản như cộng, trừ, nhân, chia, lũy thừa và gốc (với số mũ / độ nguyên) - mỗi thao tác như vậy trên các số nguyên / số thập phân "lớn" được tính là một số chẵn nếu nó liên quan đến một hoặc nhiều vòng lặp trong nội bộ. Để rõ ràng, các hàm và quyền hạn lượng giác liên quan đến số phức không phải là hoạt động cơ bản.
  • Thuật toán dự kiến ​​sẽ có một bước khởi tạo cũng phải có số lượng hoạt động không đổi.
  • Nếu thuật toán cần thêm 1 hoặc 2 lần lặp để có được 2 n chữ số chính xác, bạn có thể thực hiện tối đa các n+2lần lặp thay vì chỉ n.
  • Nếu nó không đủ rõ ràng, sau 2 n chữ số chính xác , chương trình của bạn không được in bất cứ thứ gì khác (chẳng hạn như nhiều chữ số chính xác hơn, chữ số sai hoặc các tác phẩm hoàn chỉnh của Shakespeare).
  • Chương trình của bạn phải hỗ trợ các giá trị ntừ 1 đến ít nhất 20.
  • Chương trình của bạn không nên mất hơn một giờ cho n= 20 trên một máy tính hiện đại (không phải là một quy tắc cứng, nhưng hãy cố gắng giữ cho nó hợp lý).
  • Chương trình không được lấy quá 20 chữ số chính xác sau khi khởi tạo và lặp lại lần đầu tiên của thuật toán.
  • Chương trình phải được chạy trong Linux bằng phần mềm có sẵn miễn phí.
  • Mã nguồn chỉ được sử dụng các ký tự ASCII.

Ghi điểm:

Mã golf đơn giản, mã ngắn nhất thắng.

Người chiến thắng:

Người chiến thắng là Digital Trauma, cuối cùng tôi đã hoàn thành việc chạy mã của mình trên n = 20 (chỉ đùa thôi). Giải đặc biệt thuộc về primo cho giải pháp python rất nhanh và thuật toán khác nhau :)


1
Hội tụ bậc hai là lỗi ~ N ^ (1/2) . Những gì bạn mô tả là lỗi hội tụ theo cấp số nhân ~ 2 ^ (- N) .
yo '

@yo 'bạn đang nói rằng wikipedia là sai?
aditsu 18/03/2015

1
Gây hiểu lầm, ít nhất là để nói: "hội tụ bậc hai" là ~q^(n^2)theo phần 1 ở đó và ~q^2theo phần 2 ở đó.
yo '

1
Tôi không hiểu codegolf: chắc chắn bất cứ ai cũng có thể viết ngôn ngữ lập trình riêng của họ cho một nhiệm vụ như thế này, sau đó viết một chương trình, giả sử, 0 byte?
theonlygusti

2
@theonlygusti đó sẽ là một kẽ hở tiêu chuẩn và sẽ bị loại
aditsu 18/03/2015

Câu trả lời:


14

dc, 99 byte

Chơi gôn

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

Với khoảng trắng và nhận xét cho "khả năng đọc":

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

dccần phải nói có bao nhiêu chữ số chính xác nên được sử dụng. Độ chính xác tính toán cần phải cao hơn độ chính xác hiển thị cuối cùng, do đó độ chính xác tính toán được đặt thành 2^(n+1)chữ số. Tôi đã xác minh tính chính xác của đầu ra với n = 10 so với http://www.angio.net/pi/digits/pi1000000.txt .

Điều này chậm lại đáng kể cho n lớn hơn; n = 12 mất 1,5 phút trên VM của tôi. Chạy một vài mẫu khác nhau cho thấy độ phức tạp thời gian là O (e ^ n) (không đáng ngạc nhiên). Phép ngoại suy này thành n = 20 sẽ có thời gian chạy là 233 ngày. Ồ tốt Ít nhất là tốt hơn cái chết của vũ trụ.

Đây là môn đánh gôn vừa phải - ngăn xếp được sử dụng để loại bỏ các biến tạm thời trong quá trình tính toán của mỗi lần lặp, nhưng có thể sử dụng nhiều ngăn xếp hơn để rút ngắn điều này hơn.

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

Nếu bạn không thích dcgói đầu ra ở 70 ký tự, bạn có thể đặt biến môi trường DC_LINE_LENGTHthành 0:

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

2
Haha, "dễ đọc." Không thực sự áp dụng cho dc. ;)
Alex A.

Có vẻ như để in nhiều hơn 32 chữ số để nhập 5
aditsu

Tôi đã thêm một quy tắc cho điều đó, cộng với một quy tắc khác về thời gian chạy (nhưng không thực sự nghiêm ngặt). Tôi cũng không thích cách đầu ra của bạn được chia thành nhiều dòng với dấu gạch chéo ngược, đó có phải là giới hạn của dc không?
aditsu 18/03/2015

Tôi sợ đầu ra sai cho n = 6
aditsu 18/03/2015

1
Tuyệt vời, và bạn đã nhận được nó dưới 100 quá :) Bạn cũng có thể đăng chương trình 99-char golf thực tế mà không có khoảng trắng / nhận xét?
aditsu 18/03/2015

10

R, 156 byte

Hãy bắt đầu bữa tiệc này ... với việc triển khai thuật toán Gauss-Legendre tuyệt đối chưa từng có.

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

Ungolfed + giải thích:

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

Các mpfr()chức năng là một phần của Rmpfrgói. Nó tạo ra một mpfrđối tượng sử dụng đối số thứ nhất làm giá trị và đối số thứ hai là số bit chính xác. Chúng tôi gán apcho 1, và bằng cách xác định tdựa trên a(và bdựa trên t), mpfrloại propogates cho tất cả bốn biến, do đó duy trì độ chính xác trong suốt.

Như đã đề cập, điều này đòi hỏi gói R Rmpfr, là từ viết tắt của "R Nhiều điểm chính xác đáng tin cậy". Gói sử dụng GMP trong nền. Thật không may, cơ sở R không có khả năng thực hiện số học có độ chính xác cao, do đó phụ thuộc gói.

Đừng có Rmpfr? Nhạt toẹt. install.packages("Rmpfr")và tất cả những giấc mơ của bạn sẽ trở thành sự thật.

Lưu ý rằng 2e6đã được chỉ định là độ chính xác. Điều đó có nghĩa là chúng tôi có độ chính xác 2.000.000 bit, đủ để duy trì độ chính xác trong ít nhất n= 20. (Lưu ý: n= 20 mất nhiều thời gian nhưng chưa đến một giờ trên máy tính của tôi.)

Cách tiếp cận ở đây theo nghĩa đen chỉ là sự hồi sinh của các công thức trên trang Wikipedia, nhưng này, chúng ta phải bắt đầu ở đâu đó.

Bất kỳ đầu vào được chào đón như mọi khi!


Tôi đã phải viết lại rất nhiều điều này nhưng tôi vẫn phải thừa nhận rằng Peter Taylor đã giúp tôi loại bỏ 70 byte khỏi điểm số đầu tiên của tôi. Theo lời của DigitalTrauma, "bùng nổ."


7

Python 2, 214 byte

Thử thách này là một cái cớ tốt để tôi học mô-đun thập phân. Các số thập phân có độ chính xác rõ ràng và có hỗ trợ căn bậc hai. Tôi đã đặt độ chính xác thành ước tính thận trọng về độ chính xác tùy thuộc vào số vòng lặp.

Cập nhật

Tôi đã cập nhật chương trình để cải thiện độ chính xác và tốc độ, với chi phí chơi golf. Bằng cách sử dụng sqrt()phương pháp thập phân và thay thế việc x**2sử dụng bằng x*x, giờ đây nhanh hơn 200 lần. Điều này có nghĩa là bây giờ nó có thể tính toán 20 vòng lặp cho kết quả một triệu chữ số trong 6,5 giờ. Các số thập phân thường có lỗi ở chữ số cuối (gây ra bởi các thao tác trên giới hạn chính xác), do đó chương trình hiện sử dụng và loại bỏ 5 chữ số phụ để chỉ in các chữ số chính xác.

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]

Chạy mẫu:

$ 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

Mã không mã hóa:

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
Hehhalf = one/two
Chấn thương kỹ thuật số

Có vẻ như bạn không in đúng số chữ số. Và tôi tự hỏi nếu sự chậm chạp là do sử dụng không cần thiết **.
aditsu

1
@aditsu, tôi đã giảm độ chính xác xuống số lượng chữ số dự kiến ​​(nhưng việc vứt bỏ độ chính xác hoàn toàn tốt từ một lần lặp lại đang khiến răng tôi bị ngứa). Đề nghị tốt về **hiệu quả. Tôi tìm thấy rất nhiều tốc độ bằng cách loại bỏ chúng. Tôi không thể đáp ứng 20 vòng trong 1 giờ. Có lẽ với pypy hoặc Cython? Hừm. Tôi sẽ xem xét điều đó.
Logic Knight

Tốt hơn nhiều :) Đối với vấn đề này, vứt bỏ độ chính xác tốt sẽ ít tệ hơn so với tiếp tục thành độ chính xác xấu. Giới hạn 1 giờ dựa trên mã kiểm tra cjam / java của tôi chạy với java 8. Có thể python không có phép nhân / chia / sqrt hiệu quả cho số lượng lớn (Karatsuba & co)?
aditsu 18/03/2015

@aditsu: Tôi tin rằng các số nguyên có karatsuba (và chỉ như vậy) - nhưng với kích thước chi 32 bit thay vì kích thước chi 64 bit. Ai biết về thập phân.

5

Python (2.7) - 131 byte

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]

Cập nhật: Bây giờ sử dụng gmpychứ không phải gmpy2. Vì bất kỳ lý do gì, trong gmpy2việc thiết lập độ chính xác cho một giá trị duy nhất sẽ không lan truyền đến các giá trị khác. Kết quả của bất kỳ phép tính nào trở lại độ chính xác của bối cảnh hiện tại. Chính xác không tuyên truyền gmpy, mà có vẻ trực quan hơn đối với tôi. Nó cũng ít dài dòng hơn đáng kể.

Sử dụng một trong nhiều thuật toán được Borwein và Borwein nghĩ ra , hơi được tái cấu trúc. n = 20 mất khoảng 11 giây trên hộp của tôi. Không phải là phương pháp hiệu quả nhất, nhưng vẫn không tệ.


Tái cấu trúc

Thuật toán ban đầu là như sau:




Tái cấu trúc được thực hiện tăng dần, nhưng kết quả cuối cùng là




Sự đơn giản hóa chính xảy ra trong p n + 1 . Nó cũng nhanh hơn một chút do đã loại bỏ một bộ phận.

Việc triển khai hiện tại đẩy một lần lặp lại n trong phép tính p n + 1 , cho phép khởi tạo khác nhau của p 0 ( 2√2 ), nhưng ngược lại.


Sử dụng mẫu

$ 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

Tuyệt vời, nhưng bạn đang thiếu một chữ số cho n = 7
aditsu 18/03/2015

Ngoài ra, nó là thuật toán này ?
aditsu 18/03/2015

@aditsu đã sửa, và đúng vậy.
Primo

Bây giờ chữ số cuối cùng sai với n = 5
aditsu 18/03/2015

1
@aditsu pip install gmpylàm việc cho tôi; gmpygmpy2là các gói riêng biệt. Tuy nhiên, nó không dựa vào sự phản đối distutils.
Primo

3

Phương pháp bc và Newton, 43 byte

Phương pháp của Newton để tìm các số không của bất kỳ hàm nào hội tụ theo phương trình bậc hai và thuật toán đơn giản hơn nhiều so với Gauss-Legendre. Về cơ bản, nó sôi sùng sục xuống:

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

Vì vậy, ở đây đi theo đoạn trích:

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

Một chút dễ đọc hơn:

/* 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)

Để kiểm tra điều này, hãy chạy bc -ltrong trình bao và dán đoạn mã trên. Hãy chuẩn bị để chờ đợi một lúc; n=20đã chạy được khoảng 5 phút và chưa có hồi kết. n=10mất khoảng 40 giây.


4
Không chắc chắn nếu sin và cosine đủ điều kiện là "các hoạt động cơ bản như cộng, trừ, nhân, chia và sức mạnh (bao gồm cả rễ)" . Tuy nhiên, nếu bạn tự lăn sin / cosine của mình, điều đó có thể được chấp nhận.
Primo

1
Mặc dù vậy, công thức gọn gàng - nó nói rằng π là một điểm cố định của f (x) = x - tan (x)
Casey Chu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.