Хороші раціональні наближення пі


22

Напишіть програму, яка виводить усі хороші раціональні наближення pi з знаменником <1000000, збільшуючи порядок знаменника. a/bє "хорошим раціональним наближенням" 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}".
Конрад Боровський

2
@GlitchMr, яким чином рядок є числом з плаваючою точкою?
Пітер Тейлор

Я дуже хотів би, щоб це було розгорнуто з коментарями.
прим

Дивовижний. Найперший рядок 2\!:^2^..292^15.2/3]вже підірвав мій погляд.
примо

@PeterTaylor Зв'язали . Чи можемо ми зробити краще?
Вісімнадцять

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, ось логіка. Round[Pi, x]дає найближчу фракцію Piза кроками x. Це "список", так Round[Pi,1/Range@1*^6]це робиться для всіх дробів до 1/10 ^ 6 в порядку. Отриманий список з багатьма "поганими" раціональними наближеннями потім повторно ( //.) обробляється, видаляючи будь-які елементи, розташовані далі від pi, ніж попередній.
Mr.Wizard

Mathematica побив GolfScript. Акуратний.
ПравописD

У 61: Select[Round[f=Pi,1/Range@1*^6],If[#<f,f=#;True]&@Abs[#-Pi]&]... але марно з огляду на домінуючі упередження
доктор Белісарій,

Яр, Матьє. Нехай магія в цьому коді.
Майкл Стерн

7

Перл, 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 символів.
Ільмарі Каронен

Дійсно, $ = отримати ціле число. Вибачте, я не можу подати два рази.
Тото

Я не можу змусити це запуститись:String found where operator expected at prog.pl line 1, near "say"$=/$_""
Кіт Рендалл

@KeithRandall: Вам потрібен -M5.01перемикач (і Perl 5.10.0 або новішої версії) для sayкоманди. Вибачте, що не згадуєте про це.
Ільмарі Каронен

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 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
Кіт Рендалл

Я оновив ваші вдосконалення. Велике спасибі
Стівен Румбальський

@KeithRandall, але другий з них робить висновок порушенням специфікації.
Пітер Тейлор

При другому підході немає необхідності в змінній p. Це 4 символи.
Анте

@PeterTaylor: Я не розумію. Як це порушує специфікацію?
Стівен Румбальський

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.
Говард

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

Scala - 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
Кіт Рендалл

Ви використовуєте Scala 2.8?
користувач невідомий

Моя скала говорить "невідома версія", дивно. На ideone.com вони використовують 2.8.0, і я все одно отримую помилки.
Кіт Рендалл

Спробуйте це на простому просторі.com - працює для мене. Для skala-2.8 заміщення mathна Mathможе бути достатньою. Я згадав простоscala в цьому метатраді, якщо вам трапляється знову шукати його: meta.codegolf.stackexchange.com/a/401/373
невідомий користувач

Гаразд, це працює.
Кіт Рендалл

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 жорстко кодує відповідне представлення тривалої фракції лише на два символи.
Пітер Тейлор

Але ви не використовували тривалі дроби для вирішення цього питання, чи не так?
DavidC

Так. Це був очевидний спосіб зробити це.
Пітер Тейлор

Крім стислості, це набагато швидше, ніж більшість або всі інші рішення, які були розміщені.
Майкл Стерн

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 символів. У іншому запитанні задається програма, тож ви втратите декілька на додавання декларації класу та 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/bs, а потім відкиньте ті, які для деяких віддалені від π b'<b.

Примітка: Змініть 1e3на 1e6повний список. Іди, роби щось інше і повертайся пізніше.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.