Stern-Brocotツリーで分数の位置を見つける


11

スターン- Brocotツリーは、各画分を上記のレベルでそれを隣接する2つの画分の分子と分母を添加することにより取得された画分の二分木です。

それは始まることによって生成される0/11/0そうように、一緒にこれらの画分の分子と分母を加えることによって分画の各連続対の間の1つの部分を配置することによって、反復、「エンドポイント画分」として、そこから:

0.  0/1                                                             1/0
1.  0/1                             1/1                             1/0
2.  0/1             1/2             1/1             2/1             1/0
3.  0/1     1/3     1/2     2/3     1/1     3/2     2/1     3/1     1/0
4.  0/1 1/4 1/3 2/5 1/2 3/5 2/3 3/4 1/1 4/3 3/2 5/3 2/1 5/2 3/1 4/1 1/0

Stern-Brocotツリーの各反復(nth番目の反復)に2^n + 1は、シーケンス内の要素があり、そこから0/2^nに分数を割り当てることができ2^n/2^nます。新しい反復のたびに、連続する分数の各ペアの間に「中間」に1つの分数が挿入されます。

これにより、Stern-Brocotツリーは、正の有理数と0〜1の2進小数との間の1対1マッピングになり、2つのセットのカーディナリティが同じであるという証拠にもなります。

あなたの仕事は、最低項の正の有理数の分子と分母を与えられて、Stern-Brocotツリーにおけるその分数の位置に対応するバイナリ分数を決定するプログラムまたは関数を書くことです。

入力と出力の例を以下に示します。

2/3 -> 3/8   (4th number in iteration 3)
4/7 -> 9/32  (between 1/2 and 3/5 in the chart above)
1/1 -> 1/2   (middle number in the first iteration)

サポートする必要はありませんが、参照用に含まれている入力:

0/1 -> 0/1   (0/1 is considered the left number)
1/0 -> 1/1   (1/0 is considered the rightmost number)

この目標を達成するための、どの言語でも最短のプログラムが勝利します。


入出力要件はありますか?たとえば、参照ソリューションのように機能だけで十分ですか、それともスタンドアロンプ​​ログラムである必要がありますか?分数の出力形式は重要ですか?
ダレンストーン

機能は十分です。問題の説明でそれを明確にします。
ジョーZ.

私がそれについて考えるのは少し遅れています。明日はおそらく明確にしようと思います。
ジョーZ.


1
それを構築する別の方法は、最初の数幅優先順序でツリーのノードが1から始まる(即ち、である1/1 => 11/2 => 22/1 => 31/3 => 4、など)。そうノードに対して生成された数である場合n、次いで、2^lg n(バイナリログ)における最上位ビットのセットでありn、所望のバイナリ分率です(2*(n - 2^lg n) + 1) / 2^(lg n + 1)。(get-highest-set-bitを使用して命令セットでアセンブラーソリューションを試行する場合は、おそらくこのアプローチを使用することをお勧めします)。
ピーターテイラー

回答:


1

GolfScript(49 48 46文字)

{0\@{}{@2*2$2$>!+@@{{\}3$)*}:j~1$-j}/\)\,?}:f;

または

{0:x;\{}{.2$<!2x*+:x){\}*1$-{\}x)*}/x@)@,?}:g;

両方とも、スタック上の分子と分母を取り、スタック上の分子と分母を残す関数です。オンラインデモ

コアアイデアは、Concrete Mathematicsセクション4.5(私の版ではp122)の擬似コードで表現されています。

while m != n do
    if m < n then (output(L); n := n - m)
             else (output(R); m := m - n)

LsとRsの文字列がL = 0とR = 1のバイナリ値として解釈される場合、その値の2倍に1を加えたものが分子であり、分母は1ビット長くなります。

Golfscriptersの興味のポイントとして、これは展開が有用であるとわかったまれな機会の1つです。(わかりました、ループカウンターとしてのみ使用しますが、それは何もないよりはましです)。


1

Mathematicaの、130の114 111文字

f=#~g~0&;0~g~q_=q;p_~g~q_:=g[#,(Sign[p-#]+q)/2]&@FromContinuedFraction[ContinuedFraction@p/.{x___,n_}:>{x,n-1}]

例:

f[2/3]

3/8

f[4/7]

9/32

f[1]

1/2


1

ルビー、132 125

@JoeZのリファレンスソリューションをRubied&golfed。

def t(n,d)u=k=0;v,j,f,g,b=[1,]*5;c=2
while(z=(f*d).<=>(g*n))!=0;z>0?(j,k=f,g):(u,v=f,g);b=b*2-z;f,g=u+j,v+k;c*=2;end
[b,c]end

使用例:

>> t(2,3)
=> [3, 8]
>> t(4,7)
=> [9, 32]
>> t(1,1)
=> [1, 2]

1

Ruby(69文字) CoffeeScript(59文字)

これは、分子と分母を引数として取り、全単射後の分子と分母を含む配列を返す関数です。

g=(a,b,x=0,y=1)->c=a>=b;a&&g(a-b*c,b-a*!c,2*x+c,2*y)||[x,y]

オンラインデモ

上記のGolfScriptソリューションと同じアプローチを使用しますが、配列へのボックス化およびボックス化解除を心配することなく4つの変数を使用できるため、はるかに読みやすくなります。CoffeeScriptを選択した理由は、変数に接頭辞を付けない$(PHPで20文字を保存する)、デフォルトのパラメーター値を許可する短い関数定義構文があり(したがってf(a,b,x,y)、関数でラップする必要がないg(a,b) = f(a,b,0,1))、ブール値を整数として使用できるためです有用な値を持つ式。それを知らない人のために、CoffeeScriptには標準のCスタイルの三項演算子(C?P:Q)はありませんが、決して偽造されないC&&P||Qので、ここで置き換えるPことができます。

間違いなくよりエレガントですが、間違いなく短い、代替案は、繰り返される減算を除算とモジュロで置き換えることです:

f=(a,b,x=0,y=1,p=0)->a&&f(b%a,a,(x+p<<b/a)-p,y<<b/a,1-p)||[x+p,y]

(65文字、オンラインデモ)。このように記述すると、ユークリッドのアルゴリズムとの関係が明らかになります。


a<b1文字を節約するために括弧を使用する必要はありません。インライン化cはさらに2つを提供します。f=->a,b,x=0,y=1{...}さらに短い定義の構文を検討することもできます。
ハワード

@Howard、使用しているRubyのバージョンはわかりませんが、IDEOneでは、かっこを削除したり、その関数構文を使用しようとすると構文エラーが発生します。
ピーターテイラー

c=a<b ?後に余分なスペースを入れてみてくださいb。それ以外の場合、疑問符はの一部として扱われbます。
ハワード

0

Python-531

最後の場所の参照ソリューションとして機能するPythonの未解決のソリューション:

def sbtree(n, d): 
    ufrac = [0, 1]
    lfrac = [1, 0]
    frac = [1, 1]
    bfrac = [1, 2]
    while(frac[0] * d != frac[1] * n): 
        if(frac[0] * d > frac[1] * n): 
            # push it towards lfrac
            lfrac[0] = frac[0]
            lfrac[1] = frac[1]
            bfrac[0] = bfrac[0] * 2 - 1 
        elif(frac[0] * d < frac[1] * n): 
            # push it towards ufrac
            ufrac[0] = frac[0]
            ufrac[1] = frac[1]
            bfrac[0] = bfrac[0] * 2 + 1 
        frac[0] = ufrac[0] + lfrac[0]
        frac[1] = ufrac[1] + lfrac[1]
        bfrac[1] *= 2
    return bfrac

任意の2つの分数の中央値が常に2つの分数の値の間にあるという事実を利用して、分数間のバイナリ検索を実行します。


0

GolfScript、54文字

'/'/n*~][2,.-1%]{[{.~3$~@+@@+[\]\}*].2$?0<}do.@?'/'@,(

入力は、タスクで指定された形式でSTDINで指定する必要があります。オンラインでコードを試すことができます

> 4/7
9/32

> 9/7
35/64

> 5/1
31/32

0

Mathematica 138

alephalphaの手順ほど合理化されていませんが、これまでに作成できた最高のものでした。

q_~r~k_:=Nest[#+Sign@k/(2Denominator@# )&,q,Abs@k]  
g@d_:=
Module[{l=ContinuedFraction@d,p=-1},
l[[-1]]-=1;
(p=-p;# p)&/@l]
h[q_]:=Fold[r,1/2,g@q]

テスト中

h[2/3]
h[4/7]
h[1]

3/8
9/32
1/2


0

JavaScript 186

f=(p1,q1,p2,q2)=>{if(p1*q2+1==p2*q1){return{p:p1+p2,q:q1+q2}}let p,q,pl=0,ql=1,ph=1,qh=0;for(;;){p=pl+ph;q=ql+qh;if(p*q1<=q*p1){pl=p;ql=q}else if(p2*q<=q2*p){ph=p;qh=q}else return{p,q}}}

少ないかもしれませんが、私は読みやすいゴルフが好きです


0

Haskell、125バイト

n((a,b):(c,d):r)=(a,b):(a+c,b+d):n((c,d):r)
n a=a
z=zip[0..]
t x=[(j,2^i)|(i,r)<-z$iterate n[(0,1),(1,0)],(j,y)<-z r,x==y]!!0

オンラインでお試しください!

ペアの形式での入力と出力(n,d)

簡単な説明:

n分数の各ペアを見て、最初の行と再帰の間に新しい行を挿入することで、前の行から次の行を作成します(2番目の行を右に配置します)。基本的なケースは、基本的に単なるアイデンティティ関数であるため、非常に単純です。このt関数は、2つの境界分数だけを使用して、初期状態に基づいてその関数を無限に反復します。t次にi、各行(j)および行内の各アイテム()にインデックスを付け、探しているものと一致する最初の小数を探します。それが見つかるとj、分子と分母として得られ2^iます。

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