파이의 좋은 합리적 근사


22

분모 순서가 증가하여 분모가 <1000000 인 pi의 모든 유리한 근사값을 인쇄하는 프로그램을 작성하십시오. a/bpi가 분모가 크지 않은 다른 합리적보다 pi에 더 가까운 경우 pi의 "합리적인 근사치"입니다 b.

출력은 총 167 줄이어야하며 다음과 같이 시작하고 끝납니다.

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

최단 프로그램이 이깁니다.

답변:


23

골프 스크립트, 71 70 69 자

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

(stdin에 아무것도 전달하지 않는다고 가정)

파이에 대한 상수가없는 사람들이 더 이상 징징 거리는 소리를 듣고 싶지 않습니다. 부동 소수점 숫자조차 없습니다!

배경 은 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, 문자열은 어떤 방식으로 부동 소수점 숫자입니까?
피터 테일러

나는 이것이 주석이 풀린 것을 정말로보고 싶습니다.
primo

놀랄 만한. 첫 번째 줄은 2\!:^2^..292^15.2/3]이미 내 마음을 불었다.
primo December

@PeterTaylor 묶어 . 더 잘할 수 있을까요?
Eelvex

11

매스 매 티카, 67 63

이것은 빠르지는 않지만 기술적으로 정확하다고 생각합니다.

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

Round[π, x]의 단계에서 π에 가장 가까운 분수를 제공합니다 x. 이것은 "목록"이므로 Round[π,1/Range@1*^6]모든 분수 1/10^6에 대해 순서 대로이 작업을 수행합니다 . 많은 "나쁜"합리적인 근사치가있는 결과 목록 //.은 이전 요소보다 π에서 더 멀리있는 요소를 제거 하여 반복적으로 처리됩니다 ( ).


꽤 시원하지만 Mathematica가 없기 때문에 테스트 할 수 없습니다.
Keith Randall

@ Keith, 여기 논리가 있습니다. 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]&]... 그러나 지배적 인 편견이 주어지면 쓸모가 없습니다
Dr. belisarius

Yarr, Matie. 이 코드에서 마술이 될 것입니다.
Michael Stern

7

펄, 77 자

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

사소한 문제는 Perl에 내장 π 상수가 없기 때문에 먼저로 계산해야한다는 것 atan2(0,-1)입니다. 나는 이것이 직업에 더 적합한 언어에 의해 이길 것이라고 확신하지만, 주로 텍스트 처리를 위해 설계된 언어에는 나쁘지 않습니다.


1
당신은 변경 될 수 있습니다 9999991e63 개 문자를 저장합니다.
Toto

@ M42 : 감사합니다! 이제 82 자까지 줄였습니다.
Ilmari Karonen

정수를 얻으려면 $ =가 정말 좋습니다. 죄송합니다. 두 번 투표 할 수 없습니다.
Toto

나는 이것을 실행할 수 없다 :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

Python, 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 93 자, 다른 알고리즘

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이지만 두 번째는 출력이 스펙을 위반하게합니다.
피터 테일러

두 번째 접근법에서는 변수 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

루비 1.9, 84 자

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]*?/)}

@ 피터 테일러 당신이 맞아요. Ruby 1.9를 사용해야합니다.
Howard

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)

// 언 골프 : 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 에서 사용해보십시오 -저에게 효과적입니다. 스칼라-2.8의 경우, 교체 math로하는 것은 Math충분히있을 수 있습니다. 이 메타 스레드에 대해 간단히 스칼라를 언급했는데, 다시 검색하면 meta.codegolf.stackexchange.com/a/401/373
사용자가 알 수 없음

알았어.
Keith Randall

2

매스 매 티카 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 자 이상으로 하드 코딩합니다.
피터 테일러

그러나이 질문에 대한 해결책으로 연속 분수를 사용하지 않았습니까?
DavidC

예. 그것을하는 확실한 방법이었습니다.
피터 테일러

간결한 것 외에도 게시 된 다른 솔루션의 대부분 또는 전부보다 훨씬 빠릅니다.
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/b만든 다음 일부에 대해 π에서 더 먼 것을 버립니다 b'<b.

참고 : 변경 1e3에 대한 1e6전체 목록. 다른 일을하고 나중에 돌아 오십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.