输出所有有理数的列表


13

在所有数学中,总会有一些定理超越所有常识。其中之一是无穷大大小不同的事实。另一个有趣的事实是这样的想法,即许多看起来大小不同的无穷大实际上是相同大小的。偶数和整数一样多,有理数也一样。

这个问题的一般概念是面对无穷无尽的现实。在这个挑战中,您的程序将输出一个列表,该列表将:

  • 在任何特定时间,总有大量条目
  • 最终在整个列表中恰好包含一次(如果可以运行足够长的时间)任何特定的(非零)有理数
  • 包含无数个空插槽(列表中的条目不必要地设置为0)
  • 空插槽比例接近100%的限制
  • 对于每个正整数N,具有无限数量的位置,具有N个连续的空槽

挑战

您面临的挑战是编写尽可能短的程序,该程序将输出具有以下规则的特殊列表:

  1. 索引不是平方数的所有条目都应设置为零。因此,第一个条目将为非零,第二个和第三个条目将为零,第四个条目将为非零,依此类推。
  2. 所有有理数将采用不正确分数(例如4/5或144/13)的形式进行简化。唯一的例外是零0
  3. 如果您的程序运行足够长时间且具有足够的内存,则所有(正负)有理数最终应出现在列表中。对于任何特定的有理数,所需时间可能是任意大,但始终是有限的时间。
  4. 如果运行无限长的时间,则任何非零有理数都不会出现两次。

规则3确实允许某些变化,因为存在无限数量的不同可能的合法输出。

输出将是一行流。每行将具有以下一般形式:5: 2/3第一个数字是条目号,然后是有理数。请注意,1: 0它将始终是输出的第一行。

输出示例片段:

1: 1/1
2: 0
3: 0
4: 2/1
5: 0
6: 0
7: 0
8: 0
9: -2/1
10: 0
etc...

规则,规定和注释

这是代码高尔夫。适用标准代码高尔夫规则。同样,由于输出中允许的变化,您至少需要说明为什么您相信列表将一次包含所有可能的有理数,并且您的解决方案是正确的。

编辑:由于质数确实分散了挑战,因此我将其更改为平方数。这实现了相同的目的,并且缩短了解决方案。


1
规则1的意义是什么?您是否只想让人们相互打高尔夫两个单独的程序来测试素性并列举其原理?
彼得·泰勒

它显示了整数中的一小部分仍具有与整个有理数集相同的基数,并且还允许空插槽的百分比接近(但从未达到)100%。
PhiNotPi 2012年

我假设程序也需要在固定数量的内存中运行,也就是说,不能假设机器总是可以分配更多的内存吗?另外,当您知道列表索引具有有限范围时,是否将C int用于列表索引是否违反规则?(即使确切的限制可能因实现而异。)是否需要某种形式的bignum?
面包箱2012年

1
@PhiNotPi,这样做的方法要简单得多,这让问题更有趣。
彼得·泰勒

1
请注意,1: 0它将始终是输出的第一行。–这与您的榜样矛盾,对我也没有意义。
Wrzlprmft 2015年

Answers:


6

Haskell,184个字符

main=putStr.unlines$zip[1..](s>>=g)>>=h
s=(1,1):(s>>=f)
f(a,b)=[(a,a+b),(a+b,b)]
g x@(a,b)=[x,(-a,b)]
h(i,(a,b))=(i^2)%(u a++'/':u b):map(%"0")[i^2+1..i*(i+2)]
i%s=u i++": "++s
u=show

这会进行一次Calkin-Wilf树的广度优先遍历,从而一次生成所有正有理数的简化形式。然后,它在正负之间交替,以覆盖所有非零有理数,并在正方形条目之间填充零。

输出(为简便起见,不包括零行):

1: 1/1
4: -1/1
9: 1/2
16: -1/2
25: 2/1
36: -2/1
49: 1/3
64: -1/3
81: 3/2
100: -3/2
...

5

鼠尾草,103 113 128

贤者可以轻松列出合理性!一如既往地进行格式化以符合程序要求会破坏所有内容。

for i,q in enumerate(QQ):
 for j in[(i-1)^2+1..i*i]:print'%d:'%j,[0,'%d/%d'%(q.numer(),q.denom())][j==i*i]

Sage QQ根据其高度枚举:GCD减少后分子和分母的最大绝对值。


您可以消除x.next(),并使用print一次,如下所示,使比分到124: x=enumerate(QQ) for i,q in x: for j in[(i-1)^2+1..i*i]: print'%d: '%j,'%d/%d'%(q.numer(),q.denom())if j.is_square()else 0。这无法在评论中正确显示,但我想您可以理解我的意思。
res

顺便说一句,我注意到在前4个肯定元素之后,Sage的枚举与其他答案中的不同。Calkin-Wilf公式给出了一个序列,其中有理数的分母是下一个有理数的分子。例如(...,1/3,3/2,2/3,...),而Sage是(...,1/3,3/1,2/3,...)。我似乎找不到用于Sage枚举的任何文档,以查看其计算方式。
res

@res,谢谢!我想合并打印语句,但忘了使用[x..y]表示法。很高兴在这里看到另一个Sage用户!
12

4

Python,162

f=lambda n:f(n/2)if n%2 else f(n/2)+f(n/2-1)if n else 1
n=i=1
while 1:
 print'%d:'%i,
 if i-n*n:s=0
 else: n+=1;s='%d/%d'%((-1)**n*f(n/2-1),f(n/2))
 print s
 i+=1

这使用了Calkin&Wilf 在“ 重述理性”中给出的递归。


2

Haskell,55个字节

mapM_ print$join$iterate(>>=(\x->[x+1,1/(1+1/x)]))[1%1]

输出

1 % 1
2 % 1
1 % 2
3 % 1
2 % 3
3 % 2
1 % 3
4 % 1
...

1%1是Calkin-Wilf树的根;迭代添加每个节点的两个孩子;该联接将级别折叠到一个列表中。

如果添加适当的导入,120和负数,则为120个字符:

import Data.Ratio
import Control.Monad
main=mapM_ print$0:(join(iterate(>>=(\x->[x+1,1/(1+1/x)]))[1%1])>>=(\x->[-x,x]))

输出

0 % 1
(-1) % 1
1 % 1
(-2) % 1
2 % 1
(-1) % 2
1 % 2
(-3) % 1
3 % 1
(-2) % 3
2 % 3
(-3) % 2
3 % 2
(-1) % 3
1 % 3
(-4) % 1
4 % 1
...

输出空插槽?味道不好:(您让我进入了“所有积极理性的清单”


mapM_ print$fix((1%1:).(>>= \x->[x+1,1/(x+1)]))是47个字符。从haskellwiki。在haskell.org的“尝试” REPL 上按原样工作,没有任何输入,(没事的话mapM_ print……)
Will Ness

1

PHP 105字节

注意:此代码必须另存为iso-8859-1(ansi),才能正确运行。默认情况下,将所有输入编码为utf8的在线解释器(例如ideone)将生成错误的输出。

<?for($f=µ;$i++<$j*$j||++$j%2||(--$$f?$$f--:$f^=C);)echo"$i: ",$i==$j*$j?$j%2?$x=++$ö.~Ð.++$µ:"-$x":0,~õ;

使用Georg Cantor的枚举(对+/-值稍作修改)。

如果您在运行上述代码时遇到问题(可能是由于NOTICE消息过多),请改用它(107字节):

<?for($f=µ;$i++<$j*$j||++$j%2||(--$$f?$$f--:$f^=C);)echo"$i: ",$i==$j*$j?$j%2?$x=++$ö.'/'.++$µ:"-$x":0,'
';

1
我在此代码中出现运行时错误(它似乎包含一些奇怪的字符;例如,“ $ö。〜Ð。”)。
res 2012年

在ideone上,您能否证明该解决方案有效?我也收到错误消息:ideone.com/ru1fo
mellamokb'5

当生成太多 NOTICE消息时,Idone似乎会出错:〜Ð (等于'/')和〜õ(等于“ \ n”)都会在每次迭代时生成NOTICE。当然,如果您取消了NOTICE,这不是问题。粘贴都被替换(107字节)的信息:ideone.com/lFUbl
primo

我刚刚注意到Ideone的PHP解释器生成了错误的输出。如果您在本地运行代码,您将看到它是正确的。或者,您可以使用有效的PHP解释器对其进行测试,例如Anarchy Golf的性能检查器:golf.shinh.org/checker.html(将其保存到文件中并上传)
primo

当我将修改后的代码保存到使用ANSI编码的文件中时,它确实可以在Anarchy Golf解释器上运行。但是,现在存在一个不同的问题:它违反了列表中“任何非零有理数都不应出现两次”的要求。实际上,该代码似乎无数次列出了每个有理数。例如1 / 1、2 / 2、3 / 3,...都是相同的有理数,同样适用于1 / 2、2 / 4、3 / 6,...等
2012年

0

八度,168字节

a=b=p=1;do for i=(p-1)^2+1:p^2-1 printf("%d: 0\n",i)end
printf("%d: %d/%d\n",p^2,a,b)
a=-a;if a>0do if b==1 b=a+1;a=1;else a++;b--;end until 1==gcd(a,b)end
p++;until 0

该解决方案不是很复杂,它只是对有理数“地毯”的简单对角线遍历,舍弃了所有可以简化的分数。在一个正数之后a/b,它的反面-a/b总是打印在序列中的下一个负数之前。

所有正理性的对角遍历

由于将打印所有正的简单分数,并且将打印与它们相反的带符号分数,并且不可能将两个不同的简单分数具有相同的值,因此每个非零有理数将只打印一次。

已脱胶:

a=b=p=1
do
    for i=(p-1)^2+1:p^2-1
        printf("%d: 0\n",i)         # p=2,3,4: 1..3,5..8,10..15
    end
    printf("%d: %d/%d\n", p^2,a,b); # p=2,3,4: 4,9,16
    a=-a;
    if a>0                          # the rule is: after a/b, a>0 output -a/b
        do
            if b==1 b=a+1;a=1; else a++;b--; end
        until 1==gcd(a,b)
    end
    p++;
until 0
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.