Calcola la funzione binaria più efficiente


13

Oggi calcoleremo la funzione binaria più efficiente. Più specificamente, calcoleremo la funzione che, quando viene creata un'espressione dall'applicazione della funzione all'ingresso costante 0 o al suo stesso output, può rappresentare tutti gli interi positivi con le espressioni più brevi possibili, ponendo una priorità più alta sugli interi più piccoli.

Questa funzione è costruita come segue:

Per ogni numero intero, partendo da 1 e andando verso l'alto, scegli l'espressione più breve a cui non abbiamo ancora assegnato un output e rendi quel numero intero l'output di quell'espressione. I legami nella lunghezza dell'espressione verranno interrotti da un argomento sinistro più piccolo, quindi da un argomento destro più piccolo. Ecco come funziona:

  • Inizialmente, 1 non è assegnato. L'espressione non assegnata più corta è f(0, 0), quindi la imposteremo su 1.

  • Ora, 2 non è assegnato. Le espressioni non assegnate più brevi sono f(f(0, 0), 0)= f(1, 0)e f(0, f(0, 0))= f(0, 1). I legami sono interrotti verso argomenti di sinistra più piccoli, quindi f(0, 1) = 2.

  • L'espressione non assegnata più breve rimanente è f(f(0, 0), 0)= f(1, 0), quindi f(1, 0) = 3.

  • Ora, abbiamo esaurito le espressioni con solo 2 se f3 0s, quindi dovremo aggiungerne uno in più. Rompendo i legami con l'argomentazione di sinistra, poi con l'argomentazione di destra, otteniamo da f(0, 2) = 4allora f(0, f(0, f(0, 0))) = f(0, f(0, 1)) = f(0, 2).

  • Proseguendo, abbiamo f(0, 3) = 5, f(1, 1) = 6, f(2, 0) = 7, f(3, 0) = 8, f(0, 4) = 9, ...

Ecco una tabella che ho compilato per i primi valori:

    0  1  2  3  4  5  6  7  8
 /---------------------------
0|  1  2  4  5  9 10 11 12 13
1|  3  6 14 15 37 38 39 40 41
2|  7 16 42 43
3|  8 17 44 45
4| 18 46
5| 19 47
6| 20 48
7| 21 49
8| 22 50

Un altro modo di vederlo è che ogni output ha una dimensione, uguale alla somma delle dimensioni dei suoi input più una. La tabella viene compilata in ordine crescente di dimensioni dell'output, legami rotti minimizzando l'input sinistro e quello destro.

La tua sfida è, dato che due numeri interi non negativi come input, calcolano e producono il valore di questa funzione. Questo è il codice golf. Vince la soluzione più breve, in byte. Le scappatoie standard sono vietate.


Sembra simile a A072766 , ma differisce a partire da f (3, 1).
kennytm,

2
Questa è la prima sfida da un po 'di tempo che mi induce in qualche modo a calcolare in modo efficiente. Credo che qualcosa sia possibile con i numeri catalani, ma non riesco immediatamente a pensare a una soluzione. Hmm ...
orlp

2
Va bene, quindi non penso che sarebbe una buona risposta da golf, ma quello che puoi fare per renderlo ragionevolmente efficiente è sottrarre ripetutamente i numeri catalani dagli argomenti della funzione fino a quando sono più piccoli del prossimo numero catalano. Quindi hai trovato la lunghezza delle loro espressioni. Quindi è possibile utilizzare le funzioni di classificazione / non classificazione di questo documento , con modifiche, per calcolare il risultato. Forse dopo aver fatto tutto ciò è possibile "cancellare" frammenti di codice nel mezzo e trovare una soluzione ragionevolmente elegante.
orlp

In realtà, l'approccio del mio commento precedente non funziona. ((0, (0, (0, 0))), 0)è lessicograficamente più piccolo di (((0, 0), 0), (0, 0)), tuttavia quest'ultimo ha un lato sinistro più piccolo.
orlp

Risposte:


6

Haskell, 110 byte

f q=head[i|let c=[(-1,0)]:[[(f a,f b)|n<-[0..k],a<-c!!n,b<-c!!(k-n)]|k<-[0..]],(p,i)<-zip(concat c)[0..],p==q]

L'argomento qui è considerato la tupla (x,y). Abbastanza simile alla risposta sopra, ma l'elenco di ricerca contiene solo le coppie di indici sinistro e destro invece degli alberi.


1
Bella risposta! head[...]è [...]!!0e (p,i)<-zip(concat c)[0..]può essere abbreviato in (i,p)<-zip[0..]$id=<<c.
Laikoni,

Grazie per i miglioramenti! Sicuramente aggiungendo id=<<al repertorio :)
halfflat

5

Python 3, 154 byte

b=lambda n:[(l,r)for k in range(1,n)for l in b(k)for r in b(n-k)]+[0]*(n<2)
def f(x,y):r=sum((b(n)for n in range(1,x+y+3)),[]);return r.index((r[x],r[y]))

Non è molto veloce né molto golfoso, ma è un inizio.


5

Wow! In realtà sono riuscito a creare un algoritmo di calcolo efficiente. All'inizio non me l'aspettavo. La soluzione è abbastanza elegante. Ne deduce ripetutamente sempre di più, quindi ricorre fino al caso base di 0. In questa risposta la funzione C (n) indica il numeri catalani .

Il primo passo cruciale è riconoscere che ci sono C (0) = 1 valori di lunghezza zero (ovvero 0 stesso), C (1) = 1 valori di lunghezza uno (ovvero f (0, 0)), C (2) = 2 valori di lunghezza due (f (0, f (0, 0)) e f (f (0, 0), 0)).

Ciò significa che se stiamo cercando l'ennesima espressione e troviamo la k più grande tale che C (0) + C (1) + ... + C (k) <= n, allora sappiamo che n ha lunghezza k .

Ma ora possiamo continuare! Perché l'espressione che cerchiamo è l'espressione n - C (0) - C (1) - ... - C (k) nella sua classe di lunghezza.

Ora possiamo usare un trucco simile per trovare la lunghezza del segmento sinistro e quindi il grado all'interno di quella sottosezione. E poi ricorrere su quei ranghi che abbiamo trovato!

Ha trovato che f (5030, 3749) = 1542317211 in un battito di ciglia.

Python, non competitivo

def C(n):
    r = 1
    for i in range(n):
        r *= 2*n - i
        r //= i + 1
    return r//(n+1)

def unrank(n):
    if n == 0: return 0

    l = 0
    while C(l) <= n:
        n -= C(l)
        l += 1

    right_l = l - 1
    while right_l and n >= C(l - 1 - right_l) * C(right_l):
        n -= C(l - 1 - right_l) * C(right_l)
        right_l -= 1

    right_num = C(right_l)

    r_rank = n % right_num
    l_rank = n // right_num

    for sz in range(l - 1 - right_l): l_rank += C(sz)
    for sz in range(right_l): r_rank += C(sz)

    return (unrank(l_rank), unrank(r_rank))

def rank(e):
    if e == 0: return 0
    left, right = e

    l = str(e).count("(")
    left_l = str(left).count("(")
    right_l = str(right).count("(")
    right_num = C(right_l)

    n = sum(C(sz) for sz in range(l))
    n += sum(C(sz)*C(l - 1 - sz) for sz in range(left_l))

    n += (rank(left) - sum(C(sz) for sz in range(left_l))) * C(right_l)
    n += rank(right) - sum(C(sz) for sz in range(right_l))

    return n

def f(x, y):
    return rank((unrank(x), unrank(y)))

Sono abbastanza certo che sto facendo un sacco di calcoli inutili e molti passaggi intermedi potrebbero essere rimossi.

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.