Trova la posizione di una frazione nell'albero Stern-Brocot


11

L' albero Stern-Brocot è un albero binario di frazioni in cui ogni frazione viene acquisita aggiungendo i livelli e i denominatori delle due frazioni confinanti nei livelli sopra.

Viene generato iniziando con 0/1e 1/0come "frazioni di endpoint", e da lì, iterando posizionando una frazione tra ciascuna coppia consecutiva di frazioni aggiungendo insieme i numeratori e i denominatori di quelle frazioni, in questo modo:

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

In ogni iterazione dell'albero Stern-Brocot (la niterazione), ci sono 2^n + 1elementi nella sequenza, a cui possiamo attribuire una frazione da 0/2^na 2^n/2^n. Ogni nuova iterazione inserisce semplicemente una frazione "a metà" tra ciascuna coppia di frazioni consecutive.

Questo rende l'albero Stern-Brocot una mappatura uno-a-uno tra i numeri razionali positivi e le frazioni binarie tra 0 e 1, servendo così come prova che i due insiemi hanno la stessa cardinalità.

Il tuo compito è di scrivere un programma o una funzione che, dato il numeratore e il denominatore di un numero razionale positivo in termini più bassi, determina la frazione binaria che corrisponde alla posizione di quella frazione nell'albero Stern-Brocot.

Di seguito sono riportati esempi di ingressi e uscite:

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)

Input che non è necessario supportare, ma sono inclusi come riferimento:

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

Vince il programma più breve in qualsiasi lingua per raggiungere questo obiettivo.


Ci sono requisiti di input / output? ad es. È sufficiente una funzione come nella soluzione di riferimento o deve essere un programma autonomo? Il formato di output della frazione è importante?
Darren Stone,

Una funzione è sufficiente. Lo chiarirò nella descrizione del problema.
Joe Z.

È un po 'tardi per me a pensarci; Probabilmente proverò a chiarirlo domani.
Joe Z.

2
Ok, penso che la biiezione che hai in mente sia assegnare ad ogni profondità dell'albero un denominatore costante 2 ^ (profondità + 1) e numeratori 1, 3, 5, 7, ... da sinistra.
Peter Taylor,

1
Un modo alternativo di costruzione è di primo numero dei nodi dell'albero in ampiezza partenza ordine a 1 (cioè 1/1 => 1, 1/2 => 2, 2/1 => 3, 1/3 => 4, ecc). Se il numero così generato per un nodo è n, allora 2^lg n(registro binario) è il bit più alto impostato ne la frazione binaria desiderata è (2*(n - 2^lg n) + 1) / 2^(lg n + 1). (Chiunque stia tentando una soluzione assemblatore in un set di istruzioni con un bit get-più alto set probabilmente vorrà utilizzare questo approccio).
Peter Taylor,

Risposte:


1

GolfScript ( 49 48 46 caratteri)

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

o

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

Entrambe sono funzioni che prendono il numeratore e il denominatore nella pila e lasciano il numeratore e il denominatore nella pila. Demo online .

L'idea di base è espressa nello pseudocodice nella sezione 4.5 di Concrete Mathematics (p122 nella mia edizione):

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

Se la stringa di Ls e Rs viene interpretata come un valore binario con L = 0 e R = 1, allora il doppio di quel valore più uno è il numeratore e il denominatore è un bit più lungo.

Come punto di interesse per Golfscripters, questa è una di quelle rare occasioni in cui ho trovato utile svolgersi. (Ok, lo uso solo come contatore di loop, ma è meglio di niente).


1

Mathematica, 130 114 111 caratteri

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

Esempio:

f[2/3]

3/8

f[4/7]

9/32

f[1]

1/2


1

Rubino, 132 125

Rubied e golfed la soluzione di riferimento da @JoeZ.

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

Esempi di utilizzo:

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

1

Ruby (69 caratteri) CoffeeScript (59 caratteri)

Questa è una funzione che utilizza numeratore e denominatore come argomenti e restituisce un array contenente il numeratore e il denominatore dopo la biiezione.

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]

Demo online

Utilizza lo stesso approccio della mia soluzione GolfScript sopra, ma è molto più leggibile perché posso usare 4 variabili senza doversi preoccupare di boxe e unboxing in un array. Ho scelto CoffeeScript perché non ha il prefisso delle variabili con $(20 caratteri salvati ad es. Su PHP), ha una breve sintassi della definizione della funzione che consente i valori dei parametri predefiniti (quindi non è necessario racchiudere f(a,b,x,y)una funzione g(a,b) = f(a,b,0,1)) e mi consente di usare i booleani come numeri interi in espressioni con valori utili. Per coloro che non lo conoscono, CoffeeScript non ha l'operatore ternario in stile C standard ( C?P:Q), ma sono in grado di sostituire C&&P||Qqui perché Pnon sarà mai falso.

Un'alternativa probabilmente più elegante, ma indubbiamente meno breve, è quella di sostituire la sottrazione ripetuta con divisione e modulo:

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 caratteri; demo online ). Scrivere in questo modo espone la relazione con l'algoritmo di Euclide.


Non hai bisogno delle parentesi a<bche ti fanno risparmiare un carattere. Inline ne cdà altri due. Puoi anche considerare la sintassi f=->a,b,x=0,y=1{...}per una definizione ancora più breve.
Howard,

@Howard, non so quale versione di Ruby stai usando, ma su IDEOne ottengo un errore di sintassi se provo a rimuovere quelle parentesi o usando quella sintassi di funzione.
Peter Taylor,

Prova c=a<b ?con uno spazio aggiuntivo dopo b. Altrimenti il ​​punto interrogativo viene trattato come parte del b.
Howard,

0

Python - 531

Una soluzione non golfata in Python, da utilizzare come soluzione di riferimento all'ultimo posto:

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

Fa semplicemente una ricerca binaria tra le frazioni, sfruttando il fatto che la media di due frazioni qualsiasi sarà sempre tra i valori di quelle due frazioni.


0

GolfScript, 54 caratteri

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

L'input deve essere fornito su STDIN nella forma specificata nell'attività. Puoi provare il codice online .

> 4/7
9/32

> 9/7
35/64

> 5/1
31/32

0

Mathematica 138

Non così snello come la procedura di alephalpha, ma è stato il migliore che sono stato in grado di produrre finora.

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]

analisi

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}}}

potrebbe essere meno, ma mi piace il golf leggibile


0

Haskell , 125 byte

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

Provalo online!

Input e output sotto forma di coppia (n,d).

Breve spiegazione:

ncostruisce la riga successiva dalla precedente guardando ogni coppia di frazioni e inserendo la nuova tra la prima e la ricorsione (che metterà la seconda frazione proprio lì). Il caso di base è molto semplice poiché è fondamentalmente solo la funzione di identità. La tfunzione esegue l'iterazione in modo indefinito in base allo stato iniziale con solo le due frazioni di confine. tquindi indicizza ogni riga ( i) e ogni elemento nella riga ( j) e cerca la prima frazione che corrisponde a ciò che stiamo cercando. Quando lo trova, cede jcome numeratore e 2^icome denominatore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.