Emette l'ennesimo numero razionale secondo la sequenza Stern-Brocot


30

La sequenza Stern-Brocot è una sequenza simile a Fibonnaci che può essere costruita come segue:

  1. Inizializza la sequenza con s(1) = s(2) = 1
  2. Imposta contatore n = 1
  3. Aggiungi s(n) + s(n+1)alla sequenza
  4. Aggiungi s(n+1)alla sequenza
  5. Incremento n, tornare al passaggio 3

Ciò equivale a:

s (n) = \ begin {casi} 1 & \ textrm {if} n = 1 \ s (\ frac n 2) & \ textrm {if} n \ textrm {is even} \ s (\ frac {n-1 } 2) + s (\ frac {n + 1} 2) & \ textrm {altrimenti} \ end {casi}

Tra le altre proprietà, la sequenza Stern-Brocot può essere utilizzata per generare ogni possibile numero razionale positivo. Ogni numero razionale verrà generato esattamente una volta e apparirà sempre nei suoi termini più semplici; per esempio, 1/3è il 4 ° numero razionale nella sequenza, ma i numeri equivalenti 2/6, 3/9ecc. non appariranno affatto.

Possiamo definire l'ennesimo numero razionale come r(n) = s(n) / s(n+1), dove si s(n)trova l'ennesimo numero Stern-Brocot, come descritto sopra.

La tua sfida è scrivere un programma o una funzione che genererà l'ennesimo numero razionale generato usando la sequenza Stern-Brocot.

  • Gli algoritmi sopra descritti sono 1 indicizzati; se la voce è indicizzata 0, indicare nella risposta
  • Gli algoritmi descritti sono solo a scopo illustrativo, l'output può essere derivato nel modo che preferisci (oltre alla codifica hardware)
  • L'input può essere tramite STDIN, parametri di funzione o qualsiasi altro meccanismo di input ragionevole
  • Ouptut può essere su STDOUT, console, valore di ritorno della funzione o qualsiasi altro flusso di output ragionevole
  • L'output deve essere una stringa nel modulo a/b, dove ae bsono le voci pertinenti nella sequenza Stern-Brocot. La valutazione della frazione prima dell'output non è consentita. Ad esempio, per l'input 12, l'output dovrebbe essere 2/5, non 0.4.
  • Le scappatoie standard non sono ammesse

Questo è , quindi vincerà la risposta più breve in byte.

Casi test

I casi di test qui sono 1 indicizzati.

n    r(n)
--  ------
1    1/1
2    1/2
3    2/1
4    1/3
5    3/2
6    2/3
7    3/1
8    1/4
9    4/3
10   3/5
11   5/2
12   2/5
13   5/3
14   3/4
15   4/1
16   1/5
17   5/4
18   4/7
19   7/3
20   3/8
50   7/12
100  7/19
1000 11/39

Voce OEIS: A002487
Eccellente video di Numberphile che discute la sequenza: Frazioni Infinite


L'output può usare Trues invece di 1s?
Loovjo,

1
@Loovjo No, True/2non è una frazione valida (per quanto mi riguarda). A parte questo, Truenon è sempre 1- alcune lingue usano -1invece per evitare potenziali errori quando si applicano operatori bit per bit. [citazione necessaria]
Dal


2
@Citazione citata
mbomb007

1
@Sok ma in Python, Trueè equivalente 1e True/2sarebbe 1/2.
Leaky Nun,

Risposte:


3

Gelatina , 14 byte

3ẋḶŒpḄċ
’Ç”/⁸Ç

Provalo online!

Ooh, sembra che io possa battere la risposta accettata da @Dennis, e nella stessa lingua. Funziona usando una formula di OEIS: il numero di modi per esprimere (il numero meno 1) in iperbinario (cioè binario con 0, 1, 2 come cifre). A differenza della maggior parte dei programmi Jelly (che funzionano sia come programma completo che come funzione), questo funziona solo come programma completo (perché invia parte dell'output a stdout e restituisce il resto; quando usato come programma completo il valore restituito viene inviato allo stdout implicitamente, quindi tutto l'output si trova nello stesso posto, ma questo non funzionerebbe per l'invio di una funzione).

Questa versione del programma è molto inefficiente. È possibile creare un programma molto più veloce che funzioni per tutti gli input fino a 2ⁿ posizionando n subito dopo sulla prima riga; il programma ha prestazioni O ( n × 3ⁿ), quindi mantenere n piccolo è abbastanza importante qui. Il programma come scritto imposta n uguale all'input, che è chiaramente abbastanza grande, ma anche chiaramente troppo grande in quasi tutti i casi (ma ehi, salva i byte).

Spiegazione

Come al solito nelle mie spiegazioni su Jelly, il testo tra parentesi graffe (ad esempio {the input}) mostra qualcosa che è stato compilato automaticamente da Jelly a causa di operandi mancanti nel programma originale.

Funzione di supporto (calcola il n ° denominatore, cioè il n + numeratore 1 °):

3ẋḶŒpḄċ
3ẋ       3, repeated a number of times equal to {the argument}
  Ḷ      Map 3 to [0, 1, 2]
   Œp    Take the cartesian product of that list
     Ḅ   Convert binary (or in this case hyperbinary) to a number
      ċ  Count number of occurrences of {the argument} in the resulting list

I primi cinque byte stanno fondamentalmente solo generando tutti i possibili numeri iperbinari fino a una determinata lunghezza, ad es. Con l'ingresso 3, l'output di è [[0,1,2], [0,1,2], [0,1,2 ]] quindi il prodotto cartesiano è [[0,0,0], [0,0,1], [0,0,2], [0,1,0],…, [2,2,1], [2,2,2]]. fondamentalmente moltiplica solo l'ultima voce per 1, la penultima voce per 2, la voce antepenultima per 4, ecc., e quindi aggiunge; sebbene questo sia normalmente usato per convertire i binari in decimali, può gestire bene la cifra 2, e quindi funziona anche per iperbinari. Quindi contiamo il numero di volte in cui l'input appare nell'elenco risultante, al fine di ottenere una voce appropriata nella sequenza. (Fortunatamente, il numeratore e il denominatore seguono la stessa sequenza).

Programma principale (richiede il numeratore e il denominatore e formatta l'output):

’Ç”/⁸Ç
’Ç      Helper function (Ç), run on {the input} minus 1 (‘)
  ”/    {Output that to stdout}; start again with the constant '/'
    ⁸Ç  {Output that to stdout}; then run the helper function (Ç) on the input (⁸)

Continuo a scrivere programmi che impiegano quasi tanti byte per gestire l'I / O come fanno per risolvere il problema reale ...


Buon dolore, non stavi scherzando sul fatto che fosse inefficiente - su TIO 12 ci vogliono 20 secondi per completarlo e 13 volte completamente fuori! Accettato, anche se non riesco a verificare tutti i casi di test.
Ho

11

CJam (20 byte)

1_{_@_2$%2*-+}ri*'/\

Demo online . Si noti che questo è indicizzato 0. Per renderlo 1-indicizzato, sostituire l'iniziale 1_con T1.

Dissezione

Questo utilizza la caratterizzazione dovuta a Moshe Newman che

la frazione a(n+1)/a(n+2)può essere generata dalla frazione precedente a(n)/a(n+1) = xdi1/(2*floor(x) + 1 - x)

Se x = s/tpoi otteniamo

  1 / (2 * floor(s/t) + 1 - s/t)
= t / (2 * t * floor(s/t) + t - s)
= t / (2 * (s - s%t) + t - s)
= t / (s + t - 2 * (s % t))

Ora, se assumiamo questo se tsiamo coprimi allora

  gcd(t, s + t - 2 * (s % t))
= gcd(t, s - 2 * (s % t))
= gcd(t, -(s % t))
= 1

Quindi a(n+2) = a(n) + a(n+1) - 2 * (a(n) % a(n+1))funziona perfettamente.

1_           e# s=1, t=1
{            e# Loop...
  _@_2$%2*-+ e#   s, t = t, s + t - 2 * (s % t)
}
ri*          e# ...n times
'/\          e# Separate s and t with a /

Adoro la metodologia qui, risposta fantastica!
Ho

Se scorri più in basso la voce OEIS, troverai che Mike Stay ha già inviato quella formula.
Neil,

11

Haskell, 78 77 65 58 byte

Rubare senza vergogna l'approccio ottimizzato ci dà:

(s#t)0=show s++'/':show t
(s#t)n=t#(s+t-2*mod s t)$n-1
1#1

Grazie a @nimi per giocare a golf a pochi byte usando una funzione infix!

(Still) utilizza l'indicizzazione basata su 0.


Il vecchio approccio:

s=(!!)(1:1:f 0)
f n=s n+s(n+1):s(n+1):(f$n+1)
r n=show(s n)++'/':(show.s$n+1)

Accidenti al formato di output ... E agli operatori di indicizzazione. EDIT: E la precedenza.

Curiosità: se gli elenchi eterogenei fossero una cosa, l'ultima riga potrebbe essere:

r n=show>>=[s!!n,'/',s!!(n+1)]

Usare una guardia per legare s!!ndovrebbe essere più corto di un byte:f n|x<-s!!n=x:x+x+1:f$n+1
Laikoni il

@Laikoni s!!n+1non lo è, (s!!n)+1ma s!!(n+1)è per questo che non posso farlo: /
ThreeFx

In effetti, avrebbe dovuto essere ovvio. È solo ... così tanti s!!n!
Laikoni,

1
È possibile utilizzare ++'/':(show.s$n+1)in rper salvare un byte.
nimi,

1
Passare a una funzione infisso: (s#t)0=show..., (s#t)n=t#(s+t-2*mod s t)$n-1, r=1#1. Puoi anche omettere r, cioè l'ultima riga è giusta 1#1.
nimi,

6

Gelatina , 16 byte

L‘Hị⁸Sṭ
1Ç¡ṫ-j”/

Provalo online! o verifica tutti i casi di test .

Come funziona

1Ç¡ṫ-j”/  Main link. Argument: n

1         Set the return value to 1.
 Ç¡       Apply the helper link n times.
   ṫ-     Tail -1; extract the last two items.
     j”/  Join, separating by a slash.


L‘Hị⁸Sṭ   Helper link. Argument: A (array)

L         Get the length of A.
 ‘        Add 1 to compute the next index.
  H       Halve.
   ị⁸     Retrieve the item(s) of A at those indices.
          If the index in non-integer, ị floors and ceils the index, then retrieves
          the items at both indices.
    S     Compute the sum of the retrieved item(s).
     ṭ    Tack; append the result to A.

5

05AB1E , 34 33 25 23 byte

XX‚¹GÂ2£DO¸s¦ìì¨}R2£'/ý

Spiegazione

XX‚                        # push [1,1]
   ¹G           }          # input-1 times do
     Â                     # bifurcate
      2£                   # take first 2 elements of reversed list
        DO¸                # duplicate and sum 1st copy, s(n)+s(n+1)
           s¦              # cut away the first element of 2nd copy, s(n)
             ìì            # prepend both to list
               ¨           # remove last item in list
                 R2£       # reverse and take the first 2 elements
                    '/ý    # format output
                           # implicitly print

Provalo online

Salvato 2 byte grazie ad Adnan.


Funziona anche questo ?: XX‚¹GÂ2£DO¸s¦ìì¨}R2£'/ý.
Adnan,

@Adnan Indeed. Ho dimenticato che ýpuò formattare un elenco. Bello.
Emigna,

4

MATL , 20 byte

FT+"@:qtPXnosV47]xhh

Questo utilizza la caratterizzazione in termini di coefficienti binomiali indicati nella pagina OEIS .

L'algoritmo funziona in teoria per tutti i numeri, ma in pratica è limitato dalla precisione numerica di MATL e quindi non funziona per voci di grandi dimensioni. Il risultato è accurato 20almeno per gli input .

Provalo online!

Spiegazione

FT+      % Implicitly take input n. Add [0 1] element-wise. Gives [n n+1]
"        % For each k in [n n+1]
  @:q    %   Push range [0 1 ... k-1]
  tP     %   Duplicate and flip: push [k-1 ... 1 0]
  Xn     %   Binomial coefficient, element-wise. Gives an array
  os     %   Number of odd entries in that array
  V      %   Convert from number to string
  47     %   Push 47, which is ASCII for '\'
]        % End for each
x        % Remove second 47
hh       % Concatenate horizontally twice. Automatically transforms 47 into '\'
         % Implicitly display

4

Python 2, 85 81 byte

x,s=input(),[1,1]
for i in range(x):s+=s[i]+s[i+1],s[i+1]
print`s[x-1]`+"/"+`s[x]`

Questo invio è 1 indicizzato.

Utilizzando una funzione ricorsiva, 85 byte:

s=lambda x:int(x<1)or x%2 and s(x/2)or s(-~x/2)+s(~-x/2)
lambda x:`s(x)`+"/"+`s(x+1)`

Se un output simile True/2è accettabile, eccone uno a 81 byte:

s=lambda x:x<1 or x%2 and s(x/2)or s(-~x/2)+s(~-x/2)
lambda x:`s(x)`+"/"+`s(x+1)`

3

JavaScript (ES6), 43 byte

f=(i,n=0,d=1)=>i?f(i-1,d,n+d-n%d*2):n+'/'+d

1-indicizzati; cambia in n=1per 0 indicizzato. La pagina OEIS collegata ha una relazione di ricorrenza utile per ciascun termine in termini dei due termini precedenti; Ho semplicemente reinterpretato come una ricorrenza per ogni frazione in termini della frazione precedente. Purtroppo non abbiamo in linea TeX, quindi dovrai solo incollarlo su un altro sito per scoprire come questi formati:

un'BBun'+B-2(un'modB)

3

Python 2, 66 byte

f=lambda n:1/n or f(n/2)+n%2*f(-~n/2)
lambda n:`f(n)`+'/'+`f(n+1)`

Usa la formula ricorsiva.


3

C (GCC), 79 byte

Utilizza l'indicizzazione basata su 0.

s(n){return!n?:n%2?s(n/2):s(-~n/2)+s(~-n/2);}r(n){printf("%d/%d",s(n),s(n+1));}

Ideone


1
x?:yè un'estensione gcc.
rici,

3

In realtà, 18 byte

11,`│;a%τ@-+`nk'/j

Provalo online!

Questa soluzione utilizza la formula di Peter ed è parimenti indicizzata a 0. Grazie a Leaky Nun per un byte.

Spiegazione:

11,`│;a%τ@-+`nk'/j
11                  push 1, 1
  ,`│;a%τ@-+`n      do the following n times (where n is the input):
                      stack: [t s]
    │                 duplicate the entire stack ([t s t s])
     ;                dupe t ([t t s t s])
      a               invert the stack ([s t s t t])
       %              s % t ([s%t s t t])
        τ             multiply by 2 ([2*(s%t) s t t])
         @-           subtract from s ([s-2*(s%t) s t])
           +          add to t ([t+s-2*(s%t) t])
                      in effect, this is s,t = t,s+t-2*(s%t)
              k'/j  push as a list, join with "/"


@LeakyNun terrò fuori quel miglioramento fino a quando non ci saranno chiarimenti dal PO.
Mego

2

MATL , 32 30 byte

1i:"tt@TF-)sw@)v]tGq)V47bG)Vhh

Questo utilizza un approccio diretto: genera abbastanza membri della sequenza, seleziona i due desiderati e formatta l'output.

Provalo online!


2

R, 93 byte

f=function(n)ifelse(n<3,1,ifelse(n%%2,f(n/2-1/2)+f(n/2+1/2),f(n/2)))
g=function(n)f(n)/f(n+1)

Letteralmente l'implementazione più semplice. Lavorando un po 'sul golf.


2

m4, 131 byte

define(s,`ifelse($1,1,1,eval($1%2),0,`s(eval($1/2))',`eval(s(eval(($1-1)/2))+s(eval(($1+1)/2)))')')define(r,`s($1)/s(eval($1+1))')

Definisce una macro rtale da r(n)valutare in base alle specifiche. Non ho mai giocato a golf, ho solo codificato la formula.


2

Rubino, 49 byte

Questo è indicizzato 0 e utilizza la formula di Peter Taylor. Suggerimenti di golf benvenuti.

->n{s=t=1;n.times{s,t=t,s+t-2*(s%t)};"#{s}/#{t}"}

1

> <> , 34 + 2 = 36 byte

Dopo aver visto l'eccellente risposta di Peter Taylor, ho riscritto la mia risposta di prova (che era un imbarazzante 82 byte, usando una ricorsione molto goffa).

&01\$n"/"on;
&?!\:@}:{:@+@%2*-&1-:

Si aspetta che l'input sia presente nello stack, quindi +2 byte per il -vflag. Provalo online!


1

Ottava, 90 byte

function f(i)S=[1 1];for(j=1:i/2)S=[S S(j)+S(j+1) (j+1)];end;printf("%d/%d",S(i),S(i+1));end

1

C #, 91 90 byte

n=>{Func<int,int>s=_=>_;s=m=>1==m?m:s(m/2)+(0==m%2?0:s(m/2+1));return$"{s(n)}/{s(n+1)}";};

Lancia verso Func<int, string> . Questa è l'implementazione ricorsiva.

Ungolfed:

n => 
{
    Func<int,int> s = _ => _; // s needs to be set to use recursively. _=>_ is the same byte count as null and looks cooler.
    s = m =>
        1 == m ? m               // s(1) = 1
        : s(m/2) + (0 == m%2 ? 0 // s(m) = s(m/2) for even
        : s(m/2+1));             // s(m) = s(m/2) + s(m/2+1) for odd
    return $"{s(n)}/{s(n+1)}";
};

Modifica: -1 byte. Risulta che C # non ha bisogno di uno spazio tra returne $per le stringhe interpolate.



1

J, 29 byte

([,'/',])&":&([:+/2|]!&i.-)>:

uso

Valori elevati di n richiedono un suffisso xche indica l'uso di numeri interi estesi.

   f =: ([,'/',])&":&([:+/2|]!&i.-)>:
   f 1
1/1
   f 10
3/5
   f 50
7/12
   f 100x
7/19
   f 1000x
11/39

100conta come un "grande valore"?
dcsohl,

1
@dcsohl In questo metodo vengono calcolati i coefficienti binomiali e per n = 100, il più grande calcolato è C (72, 28) = 75553695443676829680> 2 ^ 64 e richiederà numeri interi estesi per evitare valori in virgola mobile.
miglia

1

Mathematica, 108 106 101 byte

(s={1,1};n=1;a=AppendTo;t=ToString;Do[a[s,s[[n]]+s[[++n]]];a[s,s[[n]]],#];t@s[[n-1]]<>"/"<>t@s[[n]])&

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.