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


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.