การประมาณเหตุผลที่ดีของ pi


22

เขียนโปรแกรมที่พิมพ์การประมาณเหตุผลที่ดีทั้งหมดของ pi ด้วยตัวส่วน <1000000 เพื่อเพิ่มลำดับตัวส่วน a/bคือ "เหตุผลประมาณดี" ของพี่ถ้ามันเป็นความใกล้ชิดกับปี่กว่าที่อื่น ๆ bที่มีเหตุผลกับตัวหารไม่ใหญ่กว่า

ผลลัพธ์ควรมีทั้งหมด 167 บรรทัดและเริ่มต้นและสิ้นสุดดังนี้:

3/1
13/4
16/5
19/6
22/7
179/57
...
833719/265381
1146408/364913
3126535/995207

โปรแกรมที่สั้นที่สุดชนะ

คำตอบ:


23

Golfscript, 71 70 69 ตัวอักษร

2\!:^2^..292^15.2/3]{(.)2/.9>+{\+.((}*;.}do;;]-1%{^0@{2$*+\}/"/"\n}/;

(สมมติว่าคุณไม่ผ่านสิ่งใดบน stdin)

ฉันไม่ต้องการได้ยินเสียงหอนของคนที่ไม่มีค่าคงที่ในตัวสำหรับ pi อีกต่อไป ฉันไม่มีเลขทศนิยม!

ดูhttp://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximationsสำหรับพื้นหลัง

# No input, so the stack contains ""
2\!:^2^..292^15.2/3]
# ^ is used to store 1 because that saves a char by allowing the elimination of whitespace
# Otherwise straightforward: stack now contains [2 1 2 1 1 1 292 1 15 7 3]
# Pi as a continued fraction is 3+1/(7+1/(15+1/(...)))
# If you reverse the array now on the stack you get the first 10 continuants followed by 2
# (rather than 3)
# That's a little hack to avoid passing the denominator 1000000

{
    # Stack holds: ... [c_n c_{n-1} ... c_0]
    (.)2/.9>+
    # Stack holds ... [c_{n-1} ... c_0] c_n (1+c_n)/2+((1+c_n)/2 > 9 ? 1 : 0)
    # (1+c_n)/2 > 9 is an ad-hoc approximation of the "half rule"
    # which works in this case but not in general
    # Let k = (1+c_n)/2+((1+c_n)/2 > 9 ? 1 : 0)
    # We execute the next block k times
    {
        # ... [c_{n-1} ... c_0] z
        \+.((
        # ... [z c_{n-1} ... c_0] [c_{n-1} ... c_0] z-1
    }*
    # So we now have ... [c_n c_{n-1} ... c_0] [(c_n)-1 c_{n-1} ... c_0] ...
    #                    [(c_n)-k+1 c_{n-1} ... c_0] [c_{n-1} ... c_0] c_n-k
    ;
    # Go round the loop until the array runs out
    .
}do

# Stack now contains all the solutions as CFs in reverse order, plus two surplus:
# [2 1 2 1 1 1 292 1 15 7 3] [1 2 1 1 1 292 1 15 7 3] ... [6 3] [5 3] [4 3] [3] [2] []
# Ditch the two surplus ones, bundle everything up in an array, and reverse it
;;]-1%

# For each CF...
{
    # Stack holds ... [(c_n)-j c_{n-1} ... c_0]
    # We now need to convert the CF into a rational in canonical form
    # We unwind from the inside out starting with (c_n)-j + 1/infinity,
    # representing infinity as 1/0
    ^0@
    # ... 1 0 [c_n-j c_{n-1} ... c_0]
    # Loop over the terms of the CF
    {
        # ... numerator denominator term-of-CF
        2$*+\
        # ... (term-of-CF * numerator + denominator) numerator
    }/

    # Presentation
    "/"\n
    # ... numerator "/" denominator newline
}/

# Pop that final newline to avoid a trailing blank line which isn't in the spec
;

1
ทางเทคนิค GolfScript มีทั้งเลขทศนิยมและค่าคงที่สำหรับ PI "#{Math.PI}"มันเรียกว่า
Konrad Borowski

2
@GlitchMr ในทางที่เป็นสตริงจำนวนจุดลอยตัว?
Peter Taylor

ฉันอยากจะเห็นสิ่งนี้ยังไม่ได้ลงความเห็น
โม่

น่าอัศจรรย์ บรรทัดแรกที่2\!:^2^..292^15.2/3]พัดใจของฉัน
โม่

@PeterTaylor ผูก เราทำได้ดีกว่านี้ไหม
Eelvex

11

Mathematica, 67 63

สิ่งนี้จะไม่เร็ว แต่ฉันเชื่อว่ามันถูกต้องทางเทคนิค

Round[π,1/Range@1*^6]//.x_:>First/@Split[x,#2≥#&@@Abs[π-{##}]&]

Round[π, x]xจะช่วยให้ส่วนที่อยู่ใกล้πในขั้นตอนของ นี่คือ "listable" ดังนั้นRound[π,1/Range@1*^6]จะทำเช่นนี้สำหรับเศษส่วนทั้งหมดตาม1/10^6ลำดับ รายการผลลัพธ์ที่มีการประมาณด้วยเหตุผล "ไม่ดี" จำนวนมากจะถูก//.ประมวลผลซ้ำแล้วซ้ำอีก ( ) โดยการลบองค์ประกอบใด ๆ ที่อยู่ห่างจากπมากกว่ารายการก่อนหน้า


ค่อนข้างเท่ แต่ฉันไม่สามารถทดสอบได้เพราะฉันไม่มี Mathematica
Keith Randall

@ คี ธ นี่คือเหตุผล Round[Pi, x]ให้เศษส่วนที่ใกล้เคียงที่สุดเป็นเป็นPiขั้นxๆ นี่คือ "listable" ดังนั้นRound[Pi,1/Range@1*^6]สิ่งนี้จะเป็นเศษส่วนทั้งหมดจนถึง 1/10 ^ 6 ตามลำดับ รายการผลลัพธ์ที่มีการประมาณด้วยเหตุผล "ไม่ดี" จำนวนมากจะถูก//.ประมวลผลซ้ำแล้วซ้ำอีก ( ) โดยการลบองค์ประกอบใด ๆ ที่อยู่ห่างจาก pi มากกว่ารายการก่อนหน้า
Mr.Wizard

Mathematica ตี GolfScript เรียบร้อย
SpellingD

ใน 61: Select[Round[f=Pi,1/Range@1*^6],If[#<f,f=#;True]&@Abs[#-Pi]&]... แต่ไร้ประโยชน์ที่ได้รับอคติที่โดดเด่น
ดร. เบลิซาเรีย

Yarr, Matie มันเป็นเวทมนตร์ในรหัสนี้
Michael Stern

7

Perl, 77 ตัวอักษร

$e=$p=atan2 0,-1;($f=abs$p-($==$p*$_+.5)/$_)<$e&&($e=$f,say"$=/$_")for 1..1e6

ความท้าทายที่รองลงมาก็คือว่า Perl ไม่ได้มีในตัวπatan2(0,-1)คงใช้ได้ดังนั้นครั้งแรกที่ผมได้มีการคำนวณว่ามันเป็น ฉันแน่ใจว่าสิ่งนี้จะถูกทำลายโดยภาษาที่เหมาะกับงานมากกว่า แต่มันก็ไม่เลวสำหรับภาษาที่ออกแบบมาเพื่อการประมวลผลข้อความเป็นหลัก


1
คุณสามารถเปลี่ยน999999เป็น1e6และบันทึก 3 ตัวอักษร
โตโต้

@ M42: ขอบคุณ! ลงไปที่ 82 ตัวอักษรตอนนี้
Ilmari Karonen

ดีมาก ๆ $ = เพื่อรับจำนวนเต็ม ขออภัยฉันไม่สามารถโหวตได้สองครั้ง
โตโต้

ฉันไม่สามารถเรียกใช้สิ่งนี้:String found where operator expected at prog.pl line 1, near "say"$=/$_""
Keith Randall

@KeithRandall: คุณต้องการ-M5.01สวิตช์ (และ Perl 5.10.0 หรือใหม่กว่า) สำหรับsayคำสั่ง ขออภัยที่ไม่ได้กล่าวถึง
Ilmari Karonen

5

งูหลาม96 93 89 ตัวอักษร

a=b=d=1.
while b<=1e6:
 e=3.14159265359-a/b;x=abs(e)
 if x<d:print a,b;d=x
 a+=e>0;b+=e<0

Python 95 95ตัวอักษรอัลกอริทึมที่แตกต่างกัน

p=3.14159265359;d=1
for a in range(3,p*1e6):
 b=round(a/p);e=abs(p-a/b)
 if e<d:print a,b;d=e

หมายเหตุ:มันเป็นตัวละครน้อยที่จะเขียนมากกว่าp=3.14159265359; from math import*ยี้การนำเข้าคำเหล่านั้น!


1
เนื้อหาย่อบางส่วน: 1.0-> 1., 10**6->1e6
Keith Randall

ฉันได้อัปเดตด้วยการปรับปรุงของคุณ ขอบคุณมาก.
Steven Rumbalski

@ KeithRandall แต่ครั้งที่สองของพวกเขาทำให้การส่งออกละเมิดข้อกำหนด
Peter Taylor

ในแนวทางที่สองไม่จำเป็นต้องใช้ตัวแปร p นั่นคือ 4 ตัวอักษร
Ante

@PeterTaylor: ฉันไม่เข้าใจ มันละเมิดสเป็คอย่างไร
Steven Rumbalski

4

JS (95 ตัวอักษร)

for(i=k=1,m=Math;i<1e6;i++)if((j=m.abs((x=m.round(m.PI*i))/i-m.PI))<k)k=j,console.log(x+'/'+i)

มันพิมพ์ 167 บรรทัด



4

C99, 113 ตัวอักษร

main(d,n){double e=9,p=2*asin(1),c,a=1;for(;n=d*p+.5,c=fabsl(p-a*n/d),d<1e6;++d)c<e&&printf("%d/%d\n",n,d,e=c);}

จำเป็นต้องรวบรวม-lmและอาจเต็มไปด้วยพฤติกรรมที่ไม่ได้กำหนด แต่มันใช้ได้สำหรับฉัน


2

สกาลา - 180 ตัวอักษร

import math._
def p(z:Int,n:Int,s:Double):Unit=
if(n==1e6)0 else{val q=1.0*z/n
val x=if(abs(Pi-q)<s){println(z+"/"+n)
abs(Pi-q)}else s
if(Pi-q<0)p(z,n+1,x)else p(z+1,n,x)}
p(3,1,1)

// ungolfed: 457

val pi=math.Pi
@annotation.tailrec
def toPi (zaehler: Int = 3, nenner: Int = 1, sofar: Double=1): Unit = {
  if (nenner == 1000000) () 
  else {
    val quotient = 1.0*zaehler/nenner
    val diff = (pi - quotient)
    val adiff= math.abs (diff)
    val next = if (adiff < sofar) {
      println (zaehler + "/" + nenner) 
      adiff 
    }
    else sofar
    if (diff < 0) toPi (zaehler, nenner + 1, next) 
    else toPi (zaehler + 1, nenner, next) 
  }  
}

คำอธิบายประกอบ tailrec เป็นเพียงการตรวจสอบเพื่อตรวจสอบว่าเป็นแบบเรียกซ้ำซึ่งมักจะเป็นการปรับปรุงประสิทธิภาพ


ฉันไม่สามารถทำงานนี้ได้:pi.scala:1 error: not found: value math
Keith Randall

คุณใช้ Scala 2.8 หรือไม่
ผู้ใช้ไม่รู้จัก

สกาล่าของฉันบอกว่า "รุ่นที่ไม่รู้จัก" แปลก ใน ideone.com พวกเขาใช้ 2.8.0 และฉันยังคงได้รับข้อผิดพลาด
Keith Randall

ลองที่Simplyscala.com - ใช้ได้กับฉัน สำหรับ scala-2.8 การแทนที่mathด้วยMathอาจเพียงพอ ฉันพูดถึงเรื่องธรรมดาเกี่ยวกับเรื่องนี้ถ้าคุณบังเอิญค้นหาอีกครั้ง: meta.codegolf.stackexchange.com/a/401/373
ผู้ใช้ที่ไม่รู้จัก

ตกลงนั่นใช้ได้
Keith Randall

2

Mathematica 18ตัวอักษร 17 ตัว

ฉันเลือกที่จะใช้เป็นมาตรวัด "ดีที่สุด" จำนวนคำศัพท์ในการแสดงเศษส่วนต่อเนื่องของπ จากเกณฑ์นี้การประมาณด้วยเหตุผลที่ดีที่สุดของπคือคอนเวอร์เจนท์

มีคอนเวอร์เจนต์ 10 รายการของπโดยมีตัวหารน้อยกว่าหนึ่งล้าน นี่คือน้อยกว่า 167 ข้อกำหนดที่ร้องขอ แต่ฉันรวมมันที่นี่เพราะมันอาจเป็นที่สนใจของผู้อื่น

Convergents[π, 10] 

(* out *)
{3, 22/7, 333/106, 355/113, 103993/33102, 104348/33215, 208341/66317,
312689/99532, 833719/265381, 1146408/364913}

หากคุณต้องการเห็นตัวหารสำหรับคอนเวอร์เจนต์แรกมันจะเสียค่าใช้จ่ายเพิ่มอีก 11 ตัวอักษร:

Convergents[π, 10] /. {3 -> "3/1"}
(* out *)
{"3/1", 22/7, 333/106, 355/113, 103993/33102, 104348/33215,
208341/66317, 312689/99532, 833719/265381, 1146408/364913}

สำหรับผู้ที่มีความสนใจต่อไปนี้แสดงให้เห็นถึงความสัมพันธ์ในหมู่คอนเวอร์เจนต์หารบางส่วนและการแสดงออกของเศษส่วนต่อเนื่องของคอนเวอร์เจนซ์ของπ:

Table[ContinuedFraction[π, k], {k, 10}]
w[frac_] := Row[{Fold[(#1^-1 + #2) &, Last[#], Rest[Reverse[#]]] &[Text@Style[#, Blue, Bold, 14] & /@ ToString /@ ContinuedFraction[frac]]}];
w /@ FromContinuedFraction /@ ContinuedFraction /@ Convergents[π, 10]

เศษส่วนต่อเนื่อง

โปรดแก้ตัวการจัดรูปแบบที่ไม่สอดคล้องของเศษส่วนต่อเนื่อง


นั่นเป็นวิธีแก้ปัญหาครึ่งทาง แต่เป็นวิธีที่ง่ายที่สุด โซลูชัน GolfScript ของฉันใช้รหัสยากในการนำเสนอเศษส่วนต่อเนื่องที่เหมาะสมในอีก 2 ตัวอักษร
Peter Taylor

แต่คุณไม่ได้ใช้เศษส่วนต่อเนื่องสำหรับวิธีแก้ปัญหาสำหรับคำถามนี้ใช่ไหม
DavidC

ใช่. มันเป็นวิธีที่ชัดเจนในการทำ
Peter Taylor

นอกจากความรัดกุมแล้วนี่ยังเร็วกว่าโซลูชันอื่น ๆ ส่วนใหญ่หรือทั้งหมดที่โพสต์ไว้
Michael Stern

1

C # 140 129 ตัวอักษร

double n=3,d=1,e=d;while(n<4e5){double w=n/d-Math.PI,a=Math.Abs(w);if(a<e){e=a;Console.WriteLine(n+"/"+d);}if(w>0)d++;else n++;}

รหัสที่ไม่บีบอัด

var numerator = 3d;
var denominator = 1d;
var delta = 4d;
while (numerator < 4e5) 
{
    var newDelta = (numerator / denominator) - Math.PI;
    var absNewDelta = Math.Abs(newDelta);
    if (absNewDelta < delta)
    {
        delta = absNewDelta;
        Console.WriteLine(string.Format("{0}/{1}", numerator, denominator));
    }

    if (newDelta > 0)
    {
        denominator++;
    }
    else
    {
        numerator++;
    }
}

2
varไม่ใช่เพื่อนของคุณเสมอไป ด้วยการลบมันออกไปdoubleคุณจะได้รับความสามารถในการผสานการประกาศสูญเสียข้อกำหนดในการใช้ตัวอักษรสองตัวและสามารถบันทึกได้ 16 ตัวอักษร OTOH คำถามถามหาโปรแกรมดังนั้นคุณจะเสียเวลาเล็กน้อยในการเพิ่มการประกาศคลาสและMainเมธอด
ปีเตอร์เทย์เลอร์

1

J, 69 65

ใหม่

]`,@.(<&j{.)/({~(i.<./)@j=.|@-l)@(%~(i:3x)+<.@*l=.1p1&)"0>:_i.1e3

ยังคงเป็นวิธีการที่ดุร้าย แต่เร็วกว่าและสั้นกว่าเล็กน้อย

เก่า

"กำลังดุร้าย" ง่าย ๆ :

(#~({:<<./@}:)\@j)({~(i.<./)@j=.|@-l)@(%~(i:6x)+<.@*l=.1p1&)"0>:i.1e3

ทำให้รายการของ และแล้วทิ้งผู้ที่อยู่ไกลออกไปจากπสำหรับบางคนa/bb'<b

หมายเหตุ: เปลี่ยน1e3เป็น1e6สำหรับรายการทั้งหมด ไปทำอย่างอื่นแล้วกลับมาใหม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.