Hitung π dengan konvergensi kuadratik


20

Tulis fungsi atau program lengkap yang mengambil angka positif ndan melakukan nlangkah-langkah algoritma iteratif untuk menghitung π yang memiliki konvergensi kuadratik (yaitu kira-kira menggandakan jumlah digit akurat di setiap iterasi) kemudian mengembalikan atau mencetak 2 n digit yang benar (termasuk awal 3). Salah satu algoritma tersebut adalah algoritma Gauss-Legendre , tetapi Anda bebas menggunakan algoritma yang berbeda jika Anda mau.

Contoh:

masukan 1→ keluaran 3.1
masukan 2→ keluaran 3.141
masukan 5→ keluaran3.1415926535897932384626433832795

Persyaratan:

  • Setiap iterasi algoritma harus melakukan sejumlah operasi dasar yang konstan seperti penjumlahan, pengurangan, penggandaan, pembagian, daya dan root (dengan eksponen bilangan / derajat) - setiap operasi seperti pada bilangan bulat / angka desimal "besar" dihitung sebagai satu genap jika itu melibatkan satu atau lebih loop internal. Agar jelas, fungsi dan kekuatan trigonometri yang melibatkan bilangan kompleks bukanlah operasi dasar.
  • Algoritma ini diharapkan memiliki langkah inisialisasi yang juga harus memiliki jumlah operasi yang konstan.
  • Jika algoritme membutuhkan 1 atau 2 iterasi lebih banyak untuk mendapatkan 2 n digit yang benar, Anda dapat melakukan n+2iterasi bukan hanya n.
  • Jika itu tidak cukup jelas, setelah 2 n digit yang benar, program Anda tidak boleh mencetak yang lain (seperti angka yang lebih benar, angka yang salah atau karya Shakespeare yang lengkap).
  • Program Anda harus mendukung nilai ndari 1 hingga setidaknya 20.
  • Program Anda seharusnya tidak perlu lebih dari satu jam untuk n= 20 pada komputer modern (bukan aturan keras, tetapi cobalah untuk membuatnya masuk akal).
  • Program tidak boleh mendapatkan lebih dari 20 digit akurat setelah inisialisasi dan iterasi pertama algoritma.
  • Program harus dapat dijalankan di Linux menggunakan perangkat lunak yang tersedia secara bebas.
  • Kode sumber harus hanya menggunakan karakter ASCII.

Mencetak:

Golf kode langsung, kode terpendek menang.

Pemenang:

Pemenangnya adalah Digital Trauma, saya akhirnya selesai menjalankan kodenya pada n = 20 (hanya bercanda). Hadiah spesial diberikan kepada Primo untuk solusi python yang sangat cepat dan algoritma yang berbeda :)


1
Konvergensi kuadratik adalah kesalahan ~ N ^ (1/2) . Apa yang Anda gambarkan adalah kesalahan konvergensi eksponensial ~ 2 ^ (- N) .
yo '

@yo, maksudmu wikipedia salah?
aditsu

1
Menyesatkan, setidaknya untuk mengatakan: "konvergensi kuadratik" ~q^(n^2)sesuai dengan bagian 1 di sana dan ~q^2menurut bagian 2 di sana.
yo '

1
Saya tidak mengerti codegolf: pasti ada yang bisa menulis bahasa pemrograman sendiri khusus untuk satu tugas seperti ini, lalu menulis program, katakanlah, 0 byte?
theonlygusti

2
@theonlygusti yang akan menjadi celah standar dan akan didiskualifikasi
aditsu

Jawaban:


14

dc, 99 byte

Golf:

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

Dengan spasi putih dan komentar untuk "keterbacaan":

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

dcperlu diberi tahu berapa digit angka presisi yang harus digunakan. Ketelitian perhitungan harus lebih tinggi dari presisi tampilan akhir, sehingga ketepatan penghitungan diatur menjadi 2^(n+1)digit. Saya telah memverifikasi keakuratan hasil dengan n = 10 terhadap http://www.angio.net/pi/digits/pi1000000.txt .

Ini melambat secara dramatis untuk n yang lebih besar; n = 12 membutuhkan 1,5 menit pada VM saya. Menjalankan beberapa sampel berbeda menunjukkan kompleksitas waktu adalah O (e ^ n) (tidak mengejutkan). Mengekstrapolasi ini ke n = 20 akan memiliki runtime 233 hari. Baiklah. Setidaknya lebih baik daripada panas-mati-alam semesta.

Ini cukup golf - stack digunakan untuk menghilangkan variabel sementara selama perhitungan setiap iterasi, tetapi ada kemungkinan lebih banyak menggunakan stack untuk mempersingkat ini lebih lanjut.

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

Jika Anda tidak suka dcmembungkus output pada 70 karakter, Anda dapat mengatur variabel lingkungan DC_LINE_LENGTHke 0:

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

2
Haha, "keterbacaan." Tidak benar-benar berlaku untuk dc. ;)
Alex A.

Tampaknya untuk mencetak lebih dari 32 digit untuk input 5
aditsu

Saya menambahkan aturan untuk itu, ditambah satu lagi tentang waktu berjalan (tapi tidak terlalu ketat). Saya juga tidak suka bagaimana output Anda dibagi menjadi beberapa baris dengan garis miring terbalik, apakah itu batasan dc?
aditsu

Saya khawatir outputnya salah untuk n = 6
aditsu

1
Hebat, dan Anda mendapatkannya di bawah 100 juga :) Bisakah Anda juga memposting program 99-char golf sebenarnya tanpa spasi / komentar?
aditsu

10

R, 156 byte

Mari kita mulai pesta ini ... dengan implementasi mutlak algoritma Gauss-Legendre yang pernah ada.

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

Penjelasan + tidak dikumpulkan:

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

The mpfr()Fungsi adalah bagian dari Rmpfrpaket. Ini menciptakan mpfrobjek menggunakan argumen pertama sebagai nilai dan argumen kedua sebagai jumlah bit presisi. Kami menetapkan adan pke 1, dan dengan mendefinisikan tberdasarkan a(dan bberdasarkan pada t), mpfrtipe ini menyebar ke keempat variabel, dengan demikian menjaga presisi di seluruh.

Seperti yang disebutkan, ini membutuhkan paket R Rmpfr, yang merupakan akronim untuk "R Multiple Precision Floating point Reliable." Paket ini menggunakan GMP di latar belakang. Sayangnya basis R tidak memiliki kemampuan untuk melakukan aritmatika presisi tinggi, karenanya ketergantungan paket.

Tidak punya Rmpfr? Tanpa keringat. install.packages("Rmpfr")dan semua impian Anda akan terwujud.

Perhatikan bahwa 2e6itu ditentukan sebagai presisi. Itu berarti kita memiliki 2.000.000 bit presisi, yang cukup untuk mempertahankan presisi setidaknya n= 20. (Catatan: n= 20 membutuhkan waktu lama tetapi kurang dari satu jam di komputer saya.)

Pendekatan di sini secara harfiah hanya regurgitasi formula pada halaman Wikipedia, tapi hei, kita harus mulai dari suatu tempat.

Setiap masukan diterima seperti biasa!


Saya harus menulis ulang banyak hal ini tetapi saya masih harus mengakui bahwa Peter Taylor membantu saya mengetuk 70 byte dari skor pertama saya. Dalam kata-kata DigitalTrauma, "booming."


7

Python 2, 214 byte

Tantangan ini menyajikan alasan yang baik bagi saya untuk mempelajari modul Desimal. Angka desimal memiliki presisi yang dapat ditentukan dan memiliki dukungan root kuadrat. Saya telah mengatur presisi ke perkiraan konservatif dari keakuratan tergantung pada jumlah loop.

Memperbarui

Saya telah memperbarui program untuk meningkatkan akurasi dan kecepatan, dengan mengorbankan golf. Dengan menggunakan sqrt()metode desimal dan mengganti x**2penggunaan dengan x*x, sekarang 200 kali lebih cepat. Ini berarti sekarang dapat menghitung 20 loop memberikan hasil satu juta digit dalam 6,5 jam. Angka desimal sering memiliki kesalahan pada digit terakhir (disebabkan oleh operasi pada batas presisi), sehingga program sekarang menggunakan dan membuang 5 digit tambahan sehingga hanya digit akurat yang dicetak.

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]

Contoh dijalankan:

$ 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

Kode yang tidak dipisahkan:

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
Digital Trauma

Tampaknya Anda tidak mencetak jumlah digit yang benar. Dan saya ingin tahu apakah kelambatan ini disebabkan oleh penggunaan yang tidak perlu **.
aditsu

1
@ Aditsu, saya telah mengurangi keakuratan angka yang diharapkan (tetapi membuang akurasi yang sangat baik dari iterasi membuat gigi saya gatal). Saran bagus tentang **efeknya. Saya menemukan banyak kecepatan dengan menyingkirkannya. Saya tidak dapat memenuhi 20 loop dalam 1 jam. Mungkin dengan pypy atau Cython? Hmmm. Saya akan mempertimbangkan itu.
Logic Knight

Jauh lebih baik :) Untuk masalah ini, membuang akurasi yang baik tidak terlalu jahat daripada melanjutkan ke akurasi yang buruk. Batas 1 jam didasarkan pada kode uji cjam / java saya yang dijalankan dengan java 8. Mungkin python tidak memiliki multiplikasi / pembagian / sqrt yang efisien untuk angka besar (Karatsuba & co)?
aditsu

@ Aditsu: Saya percaya bilangan bulat memiliki karatsuba (dan hanya itu) - tetapi dengan ukuran tungkai 32-bit daripada ukuran tungkai 64-bit. Siapa yang tahu tentang Desimal.

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]

Perbarui: Sekarang menggunakan gmpydaripada gmpy2. Untuk alasan apa pun, dalam gmpy2menetapkan presisi pada nilai tunggal tidak menyebar ke nilai lain. Hasil perhitungan apa pun akan kembali ke ketepatan konteks saat ini. Presisi memang menyebar gmpy, yang tampaknya lebih intuitif bagi saya. Itu juga jauh lebih sedikit bertele-tele.

Menggunakan salah satu dari banyak algoritme yang dibuat oleh Borwein dan Borwein , sedikit direactored. n = 20 membutuhkan waktu sekitar 11 detik di kotak saya. Bukan metode yang paling efisien, tetapi masih tidak buruk.


Refactoring

Algoritma asli adalah sebagai berikut:




Refactoring dilakukan secara bertahap, tetapi hasil akhirnya adalah itu




Penyederhanaan utama terjadi pada p n +1 . Ini juga sedikit lebih cepat karena menghilangkan divisi.

Implementasi saat mendorong sebuah n kembali satu iterasi dalam perhitungan p n + 1 , yang memungkinkan untuk inisialisasi berbeda p 0 ( 2√2 ), tetapi jika tidak identik.


Contoh Penggunaan

$ 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

Hebat, tetapi Anda kehilangan satu digit untuk n = 7
aditsu

Juga, apakah ini algoritma ini ?
aditsu

@aditsu diperbaiki, dan ya benar.
Primo

Sekarang digit terakhir salah untuk n = 5
aditsu

1
@aditsu pip install gmpybekerja untuk saya; gmpydan gmpy2merupakan paket terpisah. Namun, itu bergantung pada yang sudah usang distutils.
Primo

3

bc dan metode Newton, 43 byte

Metode Newton untuk menemukan nol fungsi apa pun menyatu secara kuadratik dan algoritmanya lebih sederhana daripada Gauss-Legendre. Pada dasarnya bermuara pada:

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

Jadi begini cuplikan yang sesuai:

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

Sedikit lebih mudah dibaca:

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

Untuk mengujinya, jalankan bc -lshell dan tempelkan cuplikan di atas. Bersiaplah untuk menunggu sebentar; n=20telah berjalan sekitar 5 menit sekarang dan belum berakhir, belum. n=10membutuhkan waktu sekitar 40-an.


4
Tidak yakin apakah sinus dan cosinus memenuhi syarat sebagai "operasi dasar seperti penambahan, pengurangan, perkalian, pembagian, dan kekuasaan (termasuk root)" . Namun, jika Anda menggulung sinus / cosinus Anda sendiri, itu mungkin bisa diterima.
Primo

1
Rumus rapi, meskipun - dikatakan bahwa π adalah titik tetap dari f (x) = x - tan (x)
Casey Chu
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.