pi的良好有理逼近


22

编写一个程序,以分母递增的顺序打印出分母<1000000的pi的所有良好有理近似值。 a/b如果pi比分母不大于的任何其他有理数更接近pi,则它是pi的“良好有理近似值” b

输出应该总共有167行,并且开始和结束是这样的:

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

最短的程序获胜。

Answers:


23

Golfscript,71 70 69个字符

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

(假设您没有在stdin上传递任何内容)

我不想再听到那些没有pi内置常量的人的抱怨。我什至没有浮点数!

有关背景,请参见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,字符串以什么方式是浮点数?
彼得·泰勒

我真的很想看到这个带有评论的内容。
2012年

惊人。第一行2\!:^2^..292^15.2/3]已经让我震惊。
primo 2012年

@PeterTaylor 。我们可以做得更好吗?
Eelvex 2014年

11

Mathematica,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]给出最接近的分数。这是“可列出的”,因此对于所有小到1/10 ^ 6的分数都是如此。然后,通过删除比pi更远离pi的任何元素来重复()处理具有许多“不良”有理近似值的结果列表。PixRound[Pi,1/Range@1*^6]//.
威兹德先生2011年

Mathematica击败了GolfScript。整齐。
SpellingD 2012年

61:Select[Round[f=Pi,1/Range@1*^6],If[#<f,f=#;True]&@Abs[#-Pi]&]... ...但由于主要偏见而无用
belisarius博士2014年

Yarr,Matie。在此代码中,变魔术。
Michael Stern

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
您可以更改9999991e6并保存3个字符。
Toto

@ M42:谢谢!现在降至82个字符。
Ilmari Karonen 2011年

真的很好,$ =获取整数。抱歉,我不能两次投票。
多托

我无法执行此操作:String found where operator expected at prog.pl line 1, near "say"$=/$_""
Keith Randall

@KeithRandall:您需要-M5.01用于say命令的开关(以及Perl 5.10.0或更高版本)。对不起,我没有提及。
Ilmari Karonen 2011年

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*。该死的罗imports进口!


1
一些简化:1.0-> 1.10**6->1e6
Keith Randall

我已更新您的改进。非常感谢。
史蒂芬·鲁姆巴尔斯基

@KeithRandall,但是其中第二个使输出违反规范。
彼得·泰勒

在第二种方法中,不需要变量p。那是4个字符。
安特

@PeterTaylor:我不明白。它如何违反规范?
Steven Rumbalski 2011年

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

@Peter Taylor你是对的。您必须使用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
Keith Randall

您在使用Scala 2.8吗?
用户未知,

我的scala说“未知版本”,很奇怪。在ideone.com上,他们使用2.8.0,但仍然出现错误。
基思·兰德尔

simplescala.com上尝试-对我有用。斯卡拉-2.8,替换mathMath可能就足够了。我在此元线程上提到过simplescala,如果您碰巧要再次搜索它:meta.codegolf.stackexchange.com/a/401/373
用户未知

好的,行得通。
基思·兰德尔

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]

连续分数

请原谅连续分数的格式不一致。


这大约是解决方案的一半,但这是最简单的一半。My GolfScript解决方案仅用2个字符来硬编码连续分数的合适表示形式。
彼得·泰勒

但是您没有使用连续分数来解决这个问题,对吗?
DavidC 2012年

是。这是显而易见的方法。
彼得·泰勒

除了简洁之外,这比已发布的大多数或所有其他解决方案要快得多。
迈克尔·斯特恩

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

注:更改1e31e6完整的列表。去做别的事情,以后再回来。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.