最近的分数


24

任务:

您的程序将使用格式正确的,正的简单分数<numerator>/<denominator>

对于此输入,它必须找到两个分数。

  1. 小于输入的分数。
  2. 大于输入的分数。

两个分数的分母都必须小于输入的分母。在所有可能的分数中,它们与输入的差异应该最小。

输出:

程序的输出必须为:

  • 小于输入的小数,格式为<numerator>/<denominator>
  • 后面跟一个空格字符(ASCII码32)。
  • 后跟一个大于输入的小数,格式为<numerator>/<denominator>

如下:

«fraction that is < input» «fraction that is > input»

规则:

  • 输出的所有分数必须使用最低的术语
  • 输出的所有分数必须是正确的分数。
  • 如果规则没有允许的任何适当分数,则必须输出0而不是分数<输入,而1不是分数>输入。
  • 您可以选择是要接收分数作为命令行参数(例如yourprogram.exe 2/5)还是提示用户输入。
  • 您可能会认为您的程序不会收到无效的输入。
  • 最短的代码(以任何语言表示的字节数)为准。
  • 任何非标准的命令行参数(运行脚本通常不需要的参数)都计入总字符数。

  • 您的程序不得执行的操作:

    • 取决于任何外部资源。
    • 取决于具有特定的文件名。
    • 输出所需输出以外的任何内容。
    • 需要非常长的时间才能运行。如果您的程序在一分钟的179565/987657时间内用普通家庭用户的计算机上的分数用6位数字的分子和分母(例如)运行一分钟,则它无效。
    • 输出分数以0作为分母。你不能被零除。
    • 输出分数0作为分子。您的程序必须输出0而不是小数。
    • 减少输入分数。如果作为输入给出的分数是可约的,则必须使用输入的分数。
  • 发布此挑战之前,不得使用没有公开可用的编译器/解释器的编程语言来编写您的程序。

例子:

输入: 2/5
输出: 1/3 1/2

输入: 1/2
输出: 0 1

输入: 5/9
输出: 1/2 4/7

输入: 1/3
输出: 0 1/2

输入: 2/4
输出: 1/3 2/3

输入: 179565/987657
输出: 170496/937775 128779/708320


1
您的第一个示例不符合规范:两个分数的分母都必须低于输入的分母。
霍华德

1
第一个示例,输出应为1/3 1/2
Heiko Oberdiek

@HeikoOberdiek你是对的。固定。
user2428118 2014年

1
定义“普通家庭用户的计算机”。在1.6GHz Intel Atom计算机上接受90秒是否可以接受?
约翰·德沃夏克

2
您的最后一个示例不正确。输入分数等于输出分数的第一个。
DavidC

Answers:


3

贤者- 119 117

x,X=map(int,raw_input().split('/'))
a=0
A=c=C=1
while C<X:exec("ab,,AB"[c*X>C*x::2]+"=c,C");c=a+b;C=A+B
print a/A,b/B

仅在最后一行需要鼠尾草,这会处理输出。其他所有内容也可以在Python中使用。

替换raw_input()sys.argv[1],使输入从命令行参数而不是提示中读取。这不会更改字符数。(在未sys先导入的情况下在Python中无法使用。)

这实质上是使用现有元素的中值递归地构造相应的Farey序列,但将其自身限制为最接近输入的那些元素。从另一个角度来看,它对相应的Farey序列进行嵌套间隔搜索。

它可以在不到一秒钟的时间内正确处理所有示例。

这是一个非高尔夫版本:

x,X = map(Integer,sys.argv[1].split('/'))
x = x/X
a = 0
c = b = 1
while c.denominator() < X:
    if c > x:
        b = c
    else:
        a = c
    c = ( a.numerator() + b.numerator() ) / ( a.denominator() + b.denominator() )
print a,b

我已经担心我不会收到任何有关此赏金的新材料。做得好。
user2428118 2014年

不错的把戏exec
xnor

作为赏金期限内提交的唯一答案,我特授予您赏金。恭喜你
user2428118 2014年

我只是在其中一个示例中修复了一个错误。您可能需要更正您的提交(即使自提交以来已经半年了)。
user2428118

12

Python 2.7-138

x,y=n,d=map(int,raw_input().split('/'))
while y:x,y=y,x%y
def f(p,a=d):
 while(a*n+p)%d:a-=1
 print`(a*n+p)/d`+('/'+`a`)*(a>1),
f(-x);f(x)

我从显而易见的蛮力解决方案开始,但是我意识到,由于OP希望能够在一分钟之内用六位数字的分子和分母来解决实例,所以我需要比尝试万亿种可能性更好的解决方案。我在Wikipedia页面上找到了有关Farey序列的便捷公式:如果a / b,c / d是Farey序列之一的邻居a/b<c/d,则带有,则为b*c-a*b=1。我程序中f内部的while循环使用gcd将这个事实扩展为非约简数,而另一个while循环计算该gcd。

我已经非常努力地打高尔夫球了,但是我很想听听任何建议。

编辑:

166-> 162:删除ab从外程序。他们是不必要的。
162-> 155:str()->`` 155-
> 154:添加了k
154-> 152:x从函数内部删除,而是将其作为参数传递。
152-> 150:给出a默认值,而不是将其作为参数传递。
150-> 146:更改了x和的初始化y
146-> 145:已删除k
145-> 144:将...和...或...更改为(...,...)[...],从而节省了空间。
144-> 138:已将(...,...)[...]更改为... + ... *(...)。感谢@ mbomb007。

测试用例:

2/5
1/3 1/2

1/2
0 1

2/4
1/3 2/3

179565/987657
170496/937775 128779/708320

12345678/87654321
12174209/86436891 11145405/79132382

在我的计算机上,倒数第二个测试用时不到一秒钟,而最后一个测试用了约5-10秒。


k=1是纯粹的邪恶。
Evpok

1
@Evpok:我试图让k = y = n工作,但是显然如果您在函数内修改变量,python希望它是局部的。这是获取4个字符的局部变量的唯一方法。此外,由于该分数是正数且是正确的,所以分母不能为
1。– isaacg 2014年

命令行参数在Python中很容易使用,因此应按照此处的说明将其用于输入。
亚历克斯·桑顿

1
您可以选择是要接收分数作为命令行参数(例如yourprogram.exe 2/5)还是提示用户输入。”
isaacg 2014年

保存6个字符:print`(a*n+p)/d`+('/'+`a`)*(a>1),
mbomb007

5

Mathematica,163个字节

{a,b}=FromDigits/@InputString[]~StringSplit~"/";r=Range[b-1];""<>Riffle[#~ToString~InputForm&/@(#@DeleteCases[#2[a/b*r]/r,a/b]&@@@{{Max,Floor},{Min,Ceiling}})," "]

这严重受限于作为用户输入和字符串的输入/输出要求。在Mathematica中,处理琴弦确实很麻烦(至少在您想打高尔夫球的时候)。在Mathematica中以自然的方式执行此操作(仅使用整数和有理数),我可能会将其减小到大小的50%。

它可以在我的机器上几秒钟内完成6位数字的输入。

更具可读性(虽然并不是真正意义上的):

{a, b} = FromDigits /@ InputString[]~StringSplit~"/";
r = Range[b - 1];
"" <> Riffle[#~ToString~
     InputForm & /@ (#[DeleteCases[#2[a/b*r]/r, a/b]] & @@@ {{Max, 
       Floor}, {Min, Ceiling}}), " "]

有趣的是,以这种“自然的方式”进行操作,即作为一个使用分子和分母并返回两个有理数的函数,它只有84个字符(所以我的50%估计实际上很接近):

f[a_,b_]:=#@DeleteCases[#2[a/b*(r=Range[b-1])]/r,a/b]&@@@{{Max,Floor},{Min,Ceiling}}

3

朱莉娅-127字节

我从数学角度解决了这个问题,避免了需要循环,因此该代码对于大型输入运行得非常快(请注意:如果a / b是输入,则a * b必须适合Int64(在32位系统上为Int32) ,否则将生成无意义的答案-如果a和b都可以在Int32中表示(在32位系统上为Int16,则不会出现问题)。

更新:不再需要通过使用÷来节省div的反斜杠,净节省2个字节。

a,b=int(split(readline(),"/"));k=gcd(a,b);f=b-invmod(a÷k,b÷k);d=2b-f-b÷k;print(a*d÷b,d<2?" ":"/$d ",a*f÷b+1,"/$f"^(f>1))

取消高尔夫:

a,b=int(split(readline(),"/")) # Read in STDIN in form a/b, convert to int
k=gcd(a,b)           # Get the greatest common denominator
f=b-invmod(a÷k,b÷k)  # Calculate the denominator of the next biggest fraction
d=2b-f-b÷k           # Calculate the denominator of the next smallest fraction
print(a*d÷b,d<2?" ":"/$d ",a*f÷b+1,"/$f"^(f>1)) # Calculate numerators and print

基本思想:找到满足ad-bc = gcd(a,b)(下一个最小)和be-af = gcd(a,b)(下一个最大)的,小于b的最大d和f,然后从中计算c和e那里。除非d或f为1,否则结果输出为c / de / f。在这种情况下,将省略/ d或/ f。

有趣的是,这意味着只要输入的数据不是整数(即gcd(a,b)= a),该代码也适用于不正确的分数。

在我的系统上,输入194857602/34512958303不需要花费可观的时间来输出171085289/30302433084 23772313/4210525219


与测试55552/999999给我-396/920632 486/936509
user2428118 2014年

@ user2428118-您是否在32位系统上(或使用32位Julia)?我使用了“ int”,这意味着在32位系统上,它将使用Int32而不是Int64。int32(55552*999999)-282630400。对我来说,通过该测试,我得到51143/920632 52025/936509-注意分母是相同的,并且52025-51143 = 486-(-396)。我将添加注释以提及此问题。
Glen O

如果要确保该代码适用于所有Int64大小的输入,可以将“ int”替换为“ int128”。更改后,在中输入1234567891234567/2145768375829475878结果869253326028691/1510825213275018197 365314565205876/634943162554457681。此更改仅增加了3个额外字符。
Glen O

是的,我使用的是32位计算机。如果有时间,我会在64位计算机上尝试使用它。
user2428118 2014年

在64位计算机上进行测试可以得出正确的结果,因此我接受此答案。
user2428118 2014年

2

JavaScript 131

带有粗箭头符号和eval电话:

m=>{for(e=eval,n=e(m),i=p=0,q=1;++i</\d+$/.exec(m);)if(n*i>(f=n*i|0))g=f+1,p=f/i>e(p)?f+'/'+i:p,q=g/i<e(q)?g+'/'+i:q;return p+' '+q}

179565/987657应力测试中大约执行35秒 Firefox上,更大量的铬(〜6分钟)

更快的方法,没有eval和胖箭头表示法

for(n=eval(m=prompt(a=i=p=0,b=c=d=q=1));++i<m.match(/\d+$/);)if(n*i>(f=n*i|0))g=f+1,p=f*c>i*a?(a=f)+'/'+(c=i):p,q=g*d<i*b?(b=g)+'/'+(d=i):q;alert(p+' '+q)

179565/987657应力测试是在大约5秒执行。

不打高尔夫球:

m=prompt(); //get input
a=0; c=1; //first fraction
b=1; d=1; //second fraction
n=eval(m); //evaluate input
for (i=1; i<m.match(/\d+$/); i++) { //loop from 1 to input denominator
  f=Math.floor(n*i);
  if (n*i > f) { //if fraction not equal to simplification of input
    g=f+1; // f/i and g/i are fractions closer to input
    if (f/i>a/c) a=f, c=i;
    if (g/i<b/d) b=g; d=i; 
  }
}
alert(a+'/'+c+' '+b+'/'+d); //output values handling 0 and 1 correctly

太...了... eval。EEK
John Dvorak

3
与测试2/6给出1/3 2/5,但1/3低于而是等于 2/6
user2428118 2014年

@ user2428118已修正
Michael M.

为什么这么早就接受了这个答案?
Evpok

1
@ user2428118:您知道,您可以等待几天才能接受解决方案。而且,该解决方案不再是最短的。
isaacg 2014年

2

perl,142个字节(155个不带CPAN)

use bare A..Z;$/="/";N=<>;D=<>;F=N/D;K=G=1;for$H(1..D){J<F&&J>E?(E,I):J>F&&J<G?(G,K):()=(J=$_/H,"$_/$H")for(Z=int F*H)..Z+1}print I||0," $K\n"

或者,如果不允许使用CPAN模块/需要快3-4倍的代码:

$/="/";$N=<>;$D=<>;$F=$N/$D;$g=$G=1;for$d(1..$D){$f<$F&&$f>$E?($E,$e):$f>$F&&$f<$G?($G,$g):()=($f=$_/$d,"$_/$d")for($z=int$F*$d)..$z+1}print$e||0," $g\n"

前一个版本在我的计算机上需要9.55秒,后一个版本需要2.44秒。

不太可读:

($N, $D) = split(m[/], <>);
$F = $N / $D;
$G = 1;
foreach $d (1 .. $D) {
    $z = int $F * $d;
    foreach $_ ($z .. $z + 1) {
        $f = $_ / $d;
        ($f < $F && $f > $E ? ($E, $e) :
        ($f > $F && $f < $G ? ($G, $g) : ())) = ($f, "$_/$d");
    }
}
print $e || 0, ' ', $g || 1, "\n";
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.