最も近い分数


24

仕事:

あなたのプログラムには、形式で適切で正の単純分数が与えられます<numerator>/<denominator>

この入力では、2つの分数を見つける必要があります。

  1. 入力より少ない分数。
  2. 入力より大きい分数。

両方の分数は、入力よりも低い分母を持つ必要があります。可能なすべての小数のうち、入力との差が最も小さいはずです。

出力:

プログラムの出力は次のとおりである必要があります。

  • 入力よりも小さい、形式の分数<numerator>/<denominator>
  • 空白文字(ASCIIコード32)が続きます。
  • 次の形式で、入力より大きい分数が続きます<numerator>/<denominator>

次のように:

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

ルール:

  • 出力されるすべての小数部は最低条件でなければなりません。
  • 出力されるすべての分数は適切な分数でなければなりません。
  • ルールで許可されている適切な分数がない場合0は、分数<入力では1なく、分数>入力ではなく出力する必要があります。
  • 分数をコマンドライン引数として受け取るかどうかを選択できます(例: yourprogram.exe 2/5、ユーザー入力のプロンプトを表示。
  • あなたのプログラムが無効な入力を受け取らないと仮定するかもしれません。
  • 最短のコード(バイト単位、任意の言語)が優先されます。
  • 非標準のコマンドライン引数(通常、スクリプトの実行に必要ではない引数)は、合計文字数にカウントされます。

  • あなたのプログラムがしてはいけないこと:

    • 外部リソースに依存します。
    • 特定のファイル名を持つことに依存します。
    • 必要な出力以外を出力します。
    • 実行に非常に長い時間がかかります。プログラムが6桁の分子と分母を持つ分数で1分以上実行される場合(例:179565/987657平均的なホームユーザーのコンピューターで、それは無効です。
    • 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
最初の例は仕様と一致しません:両方の分数は入力よりも低い分母を持つ必要があります。
ハワード14

1
最初の例では、出力はになります1/3 1/2
ヘイコOberdiek 14

@HeikoOberdiekそうですね。一定。
user2428118 14

1
「平均的なホームユーザーのコンピューター」を定義します。1.6GHz Intel Atomマシンで90秒は許容されますか?
ジョンドヴォルザーク

2
最後の例は間違っています。入力端数は、出力端数の最初の端数に等しくなります。
DavidC

回答:


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()with sys.argv[1]を置き換えます。これは文字数を変更しません。(sys最初にインポートしないとPythonでは動作しません。)

これは、基本的にそれぞれのFareyシーケンスを再帰的に構築しますに、既存の要素の中央値を使用しをが、入力に最も近い要素に制限します。別の観点からは、それぞれのFareyシーケンスでネストされた間隔の検索を実行します。

私のマシンでは、すべてのサンプルが1秒未満で正しく処理されます。

以下は、バージョン化されていないバージョンです。

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 14年

と素敵なトリックexec
xnor 14年

バウンティ期間内に提出される唯一の回答として、私はあなたにバウンティを授与します。おめでとうございます。
user2428118 14年

私は、固定エラーが例の一つに。提出を修正することもできます(提出してから半年が経過している場合でも)。
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が6桁の分子と分母を持つインスタンスを1分以内に解決できるようにしたいので、1兆の可能性を試すよりも優れたソリューションが必要であることに気付きました。WikipediaページでFareyシーケンスの便利な式を見つけました。a/ b、c / dがFareyシーケンスのいずれかで隣接している場合、でa/b<c/d、でb*c-a*b=1。私のプログラムのf内のwhileループは、他のwhileループが計算するgcdを使用して、この事実を非縮小数に拡張します。

私はこれをかなり一生懸命ゴルフしましたが、何か提案があれば聞いてみたいです。

編集:

166-> 162:削除aし、b外側のプログラムから。それらは不要でした。
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秒かかりましたが、2番目から2番目のテストには1秒未満かかりました。


これk=1は純粋な邪悪です。
Evpok 14

1
@Evpok:k = y = nを動作させようとしていましたが、明らかに関数内の変数を変更する場合、Pythonはそれがローカルであることを望んでいます。これは、4文字のローカル変数を取得する唯一の方法でした。また、分数は正で適切であるため、分母を1にすることはできません。
isaacg 14

Pythonではコマンドライン引数は簡単なので、ここで説明するように入力に使用する必要があります。
アレックスソーントン14

1
「分数をコマンドライン引数(例:yourprogram.exe 2/5)として受け取るか、ユーザー入力のプロンプトを表示するかを選択できます。」
isaacg 14

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}}), " "]

この「自然な方法」、つまり分子と分母を取り、2つの有理数を返す関数としての楽しみのために、これは84文字だけです(したがって、私の50%の見積もりは実際にはかなり近かったです)。

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

3

ジュリア- 127の 125バイト

ループの必要性を避けるために数学的な観点からこれにアプローチしたため、このコードは大きな入力に対して非常に高速に実行されます(注: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です。

興味深いことに、これは、入力が整数でない限り(つまり、gcd(a、b)= a)、正の不適切な分数に対してもコードが機能することを意味します。

私のシステムでは、入力194857602/34512958303は出力に知覚可能な時間はかかりません171085289/30302433084 23772313/4210525219


でテストすること55552/999999を私に与えます-396/920632 486/936509
user2428118

@ user2428118-32ビットシステムを使用していますか(または32ビットジュリアを使用していますか)?「int」を使用しました。つまり、32ビットシステムでは、Int64ではなくInt32を使用します。int32(55552*999999)与える-282630400。私にとって、そのテストでは51143/920632 52025/936509、分母が同じであり、52025-51143 = 486-(-396)であることに注意してください。この問題に言及するメモを追加します。
グレンO

すべてのInt64サイズの入力に対してコードが機能することを確認する場合は、「int」を「int128」に置き換えることができます。その変更により、入力1234567891234567/2145768375829475878結果はになり869253326028691/1510825213275018197 365314565205876/634943162554457681ます。この変更により、3文字だけ追加されます。
グレンO

はい、32ビットコンピューターを使用しています。時間があれば、64ビットマシンで試してみます。
user2428118 14年

64ビットコンピューターでテストすると正しい結果が得られるため、この回答を受け入れます。
user2428118 14年

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
ジョンドヴォルザーク

3
でテスト2/6できますが1/3 2/5、しかし、1/3ではありません未満が、に等しいです 2/6
user2428118 14

@ user2428118修正
マイケルM. 14

なぜこんなに早くこの答えが受け入れられたのですか?
Evpok 14

1
@ user2428118:解答を受け入れるまでに数日かかることがあります。また、このソリューションはもはや最短ではありません。
isaacg 14

2

perl、142バイト(CPANなしの155)

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";
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.