Tính gần đúng hợp lý của pi


22

Viết chương trình in ra tất cả các xấp xỉ hợp lý tốt của pi với mẫu số <1000000, theo thứ tự mẫu số tăng. a/blà một "xấp xỉ hợp lý tốt" của pi nếu nó gần với pi hơn bất kỳ số hữu tỷ nào khác có mẫu số không lớn hơn b.

Đầu ra phải có tổng số 167 dòng, bắt đầu và kết thúc như thế này:

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

Chương trình ngắn nhất sẽ thắng.

Câu trả lời:


23

Golfscript, 71 70 69 ký tự

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

(Giả sử rằng bạn không vượt qua bất cứ điều gì trên stdin)

Tôi không muốn nghe thêm bất kỳ tiếng rít nào của những người không có hằng số tích hợp cho pi. Tôi thậm chí không có số dấu phẩy động!

Xem http://en.wikipedia.org/wiki/Continued_fraction#Best_rational_appro xấp xỉ cho nền.

# 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
Vâng, về mặt kỹ thuật, GolfScript có cả số dấu phẩy động và hằng số cho PI. Nó được gọi là "#{Math.PI}".
Konrad Borowski

2
@GlitchMr, chuỗi số là dấu phẩy động theo cách nào?
Peter Taylor

Tôi thực sự muốn thấy điều này không được kiểm soát với các bình luận.
primo

Kinh ngạc. Dòng đầu tiên 2\!:^2^..292^15.2/3]đã thổi vào tâm trí tôi.
Primo

@PeterTaylor Bị trói . Chúng ta có thể làm tốt hơn không?
Eelvex

11

Toán học, 67 63

Điều này sẽ không nhanh, nhưng tôi tin rằng nó đúng về mặt kỹ thuật.

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

Round[π, x]đưa ra phần gần nhất với π trong các bước của x. Đây là "có thể liệt kê" vì vậy Round[π,1/Range@1*^6]điều này cho tất cả các phân số 1/10^6theo thứ tự. Danh sách kết quả có nhiều xấp xỉ hợp lý "xấu" sau đó được lặp lại ( //.) bằng cách loại bỏ bất kỳ phần tử nào xa hơn số π so với phần trước.


Khá tuyệt, nhưng tôi không thể kiểm tra nó vì tôi không có Mathicala.
Keith Randall

@Keith, đây là logic. Round[Pi, x]đưa ra phần gần nhất Pitrong các bước của x. Đây là "có thể liệt kê", vì vậy Round[Pi,1/Range@1*^6], điều này cho tất cả các phân số xuống 1/10 ^ 6 theo thứ tự. Danh sách kết quả có nhiều xấp xỉ hợp lý "xấu" sau đó được lặp lại ( //.) bằng cách loại bỏ bất kỳ phần tử nào xa hơn pi so với phần tử trước.
Mr.Wizard

Toán học đánh bại GolfScript. Khéo léo.
Chính tả

Trong 61: Select[Round[f=Pi,1/Range@1*^6],If[#<f,f=#;True]&@Abs[#-Pi]&]... nhưng vô dụng với sự thiên vị thống trị
Tiến sĩ belisarius

Yarr, Matie. Thar là ma thuật trong mã này.
Michael Stern

7

Perl, 77 ký tự

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

Một thách thức nhỏ là Perl không có sẵn hằng số π tích hợp sẵn, vì vậy trước tiên tôi phải tính toán nó là atan2(0,-1). Tôi chắc chắn rằng điều này sẽ bị đánh bại bởi các ngôn ngữ phù hợp hơn cho công việc, nhưng nó không tệ đối với một ngôn ngữ được thiết kế chủ yếu để xử lý văn bản.


1
Bạn có thể thay đổi 999999để 1e6và tiết kiệm 3 ký tự.
Toto

@ M42: Cảm ơn! Xuống tới 82 ký tự bây giờ.
Ilmari Karonen

Thực sự tốt đẹp, $ = để lấy số nguyên. Xin lỗi, tôi không thể upvote hai lần.
Toto

Tôi không thể chạy cái này:String found where operator expected at prog.pl line 1, near "say"$=/$_""
Keith Randall

@KeithRandall: Bạn cần công -M5.01tắc (và Perl 5.10.0 trở lên) cho saylệnh. Xin lỗi vì đã không đề cập đến điều đó.
Ilmari Karonen

5

Con trăn, 96 93 89 ký tự

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 93 ký tự, thuật toán khác nhau

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

lưu ý: Đó là ít ký tự để viết p=3.14159265359;hơn from math import*. Darn những nhập khẩu dài dòng!


1
Một số rút gọn: 1.0-> 1., 10**6->1e6
Keith Randall

Tôi đã cập nhật với những cải tiến của bạn. Cảm ơn nhiều.
Steven Rumbalski

@KeithRandall, nhưng thứ hai trong số đó làm cho đầu ra vi phạm thông số kỹ thuật.
Peter Taylor

Trong cách tiếp cận thứ hai, không cần biến p. Đó là 4 ký tự.
Ante

@PeterTaylor: Tôi không hiểu. Làm thế nào để nó vi phạm thông số kỹ thuật?
Steven Rumbalski

4

JS (95 ký tự)

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)

Nó in được 167 dòng.


4

Ruby 1.9, 84 ký tự

m=1;(1..1e6).map{|d|n=(d*q=Math::PI).round;k=(n-q*d).abs/d;k<m&&(m=k;puts [n,d]*?/)}

@Peter Taylor Bạn nói đúng. Bạn phải sử dụng Ruby 1.9.
Howard

4

C99, 113 ký tự

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

Cần phải biên dịch với -lm, và có thể đầy đủ các hành vi không xác định, nhưng nó hoạt động với tôi.


2

Scala - 180 ký tự

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)

// vô dụng: 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) 
  }  
}

Chú thích tailrec chỉ là một kiểm tra, để xác minh, rằng nó là đệ quy đuôi, thường là một cải tiến hiệu suất.


Tôi không thể làm việc này:pi.scala:1 error: not found: value math
Keith Randall

Bạn đang sử dụng Scala 2.8?
người dùng không xác định

Scala của tôi nói "phiên bản chưa biết", kỳ lạ. Trên ideone.com, họ sử dụng 2.8.0 và tôi vẫn gặp lỗi.
Keith Randall

Hãy thử tại Simplyscala.com - hoạt động với tôi. Đối với scala-2.8, thay thế mathbằngMath có thể là đủ. Tôi đã đề cập đến Simplyscala trên siêu dữ liệu này, nếu bạn tình cờ tìm kiếm lại: meta.codegolf.stackexchange.com/a/401/373
người dùng chưa biết

OK, nó hoạt động.
Keith Randall

2

Toán học 18 17 ký tự

Tôi đã chọn sử dụng, như một thước đo "tốt nhất", số lượng thuật ngữ trong biểu diễn phân số tiếp tục là π. Theo tiêu chí này, các xấp xỉ hợp lý tốt nhất của π là các điểm hội tụ của nó.

Có 10 điểm hội tụ của π với mẫu số nhỏ hơn một triệu. Điều này ít hơn các điều khoản được yêu cầu là 167, nhưng tôi đưa nó vào đây vì nó có thể được người khác quan tâm.

Convergents[π, 10] 

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

Nếu bạn thực sự muốn xem mẫu số cho lần hội tụ đầu tiên, sẽ tốn thêm 11 ký tự:

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}

Đối với những người quan tâm, phần sau đây cho thấy mối quan hệ giữa các điểm hội tụ, chỉ tiêu một phần và biểu thức phân số tiếp tục của các điểm hội tụ của π:

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]

tiếp tục phân số

Vui lòng loại trừ định dạng không nhất quán của các phân số tiếp tục.


Đó là một nửa cho một giải pháp, nhưng đó là một nửa dễ dàng nhất. Giải pháp GolfScript của tôi mã hóa một đại diện phù hợp của phân số tiếp tục chỉ có 2 ký tự.
Peter Taylor

Nhưng bạn đã không sử dụng phân số tiếp tục cho giải pháp của bạn cho câu hỏi này, phải không?
DavidC

Vâng. Đó là cách rõ ràng để làm điều đó.
Peter Taylor

Ngoài việc ngắn gọn, điều này nhanh hơn nhiều so với hầu hết hoặc tất cả các giải pháp khác đã được đăng.
Michael Stern

1

C # 140 129 ký tự

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++;}

Mã không né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
varkhông phải luôn luôn là bạn của bạn. Bằng cách loại bỏ nó để giúp doublebạn có được khả năng hợp nhất các khai báo, mất yêu cầu sử dụng hai chữ và có thể lưu 16 ký tự. OTOH câu hỏi yêu cầu một chương trình, vì vậy bạn sẽ mất một vài để thêm một khai báo lớp và một Mainphương thức.
Peter Taylor

1

J, 69 65

Mới

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

Vẫn là một cách tiếp cận vũ phu nhưng nhanh hơn nhiều và ngắn hơn một chút.

Một "lực lượng vũ phu" đơn giản:

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

lập một danh sách các a/bs và sau đó loại bỏ những cái xa hơn một số cho một sốb'<b .

Lưu ý: Thay đổi 1e3thành 1e6danh sách đầy đủ. Đi làm một cái gì đó khác và trở lại sau.

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.