Хорошие рациональные приближения числа пи


22

Напишите программу, которая печатает все хорошие рациональные приближения числа Пи со знаменателем <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}/;

(Предполагается, что вы ничего не передаете на стандартный ввод)

Я не хочу больше слышать звуки от людей, у которых нет встроенных констант для пи. У меня даже нет чисел с плавающей запятой!

См. 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 Tied . Можем ли мы сделать лучше?
— 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.
— Кит Рэндалл

@ Кейт, вот логика. Round[Pi, x]дает ближайшую дробь Piв шагах x. Это «список», так Round[Pi,1/Range@1*^6]же как и для всех дробей до 1/10 ^ 6 по порядку. Результирующий список со многими «плохими» рациональными приближениями затем многократно ( //.) обрабатывается путем удаления любых элементов, которые находятся дальше от pi, чем предыдущий.
— Мистер Волшебник

Mathematica избивает GolfScript. Ухоженная.
— SpellingD

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

Ярр, Мати. Будь волшебным в этом коде.
— Майкл Стерн

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 символов.
— Илмари Каронен

Действительно хорошо, $ = получить целое число. Извините, я не могу проголосовать дважды.
— Toto

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

@KeithRandall: вам нужен -M5.01переключатель (и Perl 5.10.0 или новее) для sayкоманды. Извините, что не упомянул это.
— Илмари Каронен

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
— Кит Рэндалл

Я обновил ваши улучшения. Спасибо большое.
— Стивен Румбальски

@KeithRandall, но второй из них делает вывод нарушать спецификацию.
— Питер Тейлор

Во втором подходе нет необходимости в переменной р. Это 4 символа.
— Ante

@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

Ruby 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

Скала - 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) 
  }  
}

Хвостовая аннотация - это просто проверка, чтобы убедиться, что она хвостовая рекурсивная, что часто является улучшением производительности.


Я не могу заставить это работать:pi.scala:1 error: not found: value math
— Кит Рэндалл

Вы используете Scala 2.8?
— пользователь неизвестен

Моя скала говорит "неизвестная версия", странно. На ideone.com они используют 2.8.0, и я все еще получаю ошибки.
— Кит Рэндалл

Попробуйте это на simplyscala.com - у меня работает. Для Скале-2.8, заменив mathс Mathможет быть достаточным. Я упомянул просто scala об этой метатреде, если вам снова придется
— пользователь неизвестен

ОК, это работает.
— Кит Рэндалл

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 символа больше.
— Питер Тейлор

Но вы не использовали непрерывные дроби для решения этого вопроса, не так ли?
— 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 символов. 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/bs, а затем отбросьте те, которые находятся дальше от π для некоторых b'<b.

Примечание: Изменения 1e3в 1e6течение полного списка. Иди, сделай что-нибудь еще и вернись позже.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.