Conta gli alberi


11

Un albero è un grafico connesso e non orientato senza cicli. Il tuo compito è contare quanti alberi distinti ci sono con un dato numero di vertici.

Due alberi sono considerati distinti se non sono isomorfi . Due grafici sono isomorfi se i rispettivi vertici possono essere accoppiati in modo tale che vi sia un bordo tra due vertici in un grafico se e solo se c'è un bordo tra i vertici accoppiato a quei vertici nell'altro grafico. Per una descrizione più completa, consultare il link sopra.

Per vedere come appaiono tutti gli alberi distinti delle dimensioni da 1 a 6, dai un'occhiata qui .

La serie che si sta provando a produrre è A000055 presso l'OEIS.

Limitazione : l'esecuzione della soluzione deve richiedere un intervallo di minuti o meno sull'input 6. Questo non ha lo scopo di eliminare gli algoritmi del tempo esponenziale, ma ha lo scopo di eliminare gli algoritmi del tempo doppiamente esponenziale, come la forzatura bruta su tutti i set di bordi.

Input: qualsiasi numero intero non negativo.

L'input può essere in qualsiasi modo standard, incluso STDIN, parametro della riga di comando, input della funzione, ecc.

Output: il numero di alberi distinti con tanti vertici quanti sono gli input.

L'output può essere in qualsiasi modo standard, inclusi STDOUT, ritorno di funzione, ecc.

Esempi: 0, 1, 2, 3, 4, 5, 6, 7 dovrebbe tornare 1, 1, 1, 1, 2, 3, 6, 11.

Punteggio: codice golf, per byte. Che vinca il codice più corto!

Scappatoie standard vietate.

Risposte:


3

CJam (69 byte)

]qi:X,{1e|,:):N{N\f{1$%!*}W$.*:+}%1$W%.*:+N,/+}/W\+_1,*X=\_W%.*:+-Y/z

Demo online

Spiegazione

L'idea di base è implementare la funzione di generazione descritta in OEIS. L'input è un brutto caso speciale, ma le ultime modifiche che ho apportato hanno finito per produrre per quel caso, quindi (per valore assoluto) lo riordina. Questo è il trucco più strano qui.0-1z

.*:+viene ripetuto tre volte e sembra che potrebbe salvare un byte se estratto come {.*:+}:F~. Tuttavia, questo si interrompe con il caso speciale , perché non esegue affatto il ciclo esterno.0


Usiamo la funzione di generazione ausiliaria per A000081 , i cui termini hanno la ricorrenza

a[0] = 0
a[1] = 1
For n >= 1, a[n+1] = (sum_{k=1}^n a[n-k+1] * sum_{d|k} d * a[d]) / n

Sono sicuro che alcune lingue hanno incorporato la trasformazione inversa di Möbius , ma CJam no; l'approccio migliore che ho trovato è quello di costruire una matrice mappatura a e poi fare una moltiplicazione puntuale con utilizzo . Si noti che qui è conveniente avere inizio a partire dall'indice 1, perché vogliamo evitare la divisione per zero durante l'impostazione dei pesi. Si noti inoltre che se i due array forniti all'operazione puntuale non hanno la stessa lunghezza, i valori di quello più lungo non vengono toccati: pertanto dobbiamo prendere i primi termini di o far salire l'array di pesi fino aΣd|Kd×un'[d]dk % d == 0 ? d : 0un'.*un'Kun'n. Quest'ultimo sembra più breve. Quindi questa inversa trasformazione di Möbius rappresentaN\f{1$%!*}W$.*:+

Se chiamiamo il risultato della trasformazione inversa di Möbius M, ora abbiamo

un'[n+1]=1nΣK=1nun'[n-K+1]×M[K]

Il numeratore è ovviamente un termine di una convoluzione, quindi possiamo gestirlo invertendo una copia di o e quindi prendendo una moltiplicazione e una somma puntuali. Ancora una volta, il nostro indice varia da a , e inoltre vogliamo accoppiare gli indici che si sommano a , quindi è di nuovo conveniente indicizzare da 1 anziché a 0. Ora abbiamo consideratoun'M1nn+1un'

 qi:X,{   ,:):N{N\f{1$%!*}W$.*:+}%1$W%.*:+N,/+}/

Il punto della funzione di generazione ausiliaria è dato dalla sezione formula di A000055:

G.f.: A(x) = 1 + T(x) - T^2(x)/2 + T(x^2)/2,
where T(x) = x + x^2 + 2*x^3 + ... is the g.f. for A000081.

un'

[X=0]+un'[X]+12(un'[X/2]-Σio=0nun'[io]×un'[n-io])

un'[X/2]X1,*X=

0\+un'[0]=0X=0W\+-2un'[X]+Σio=0nun'[io]×un'[n-io]2un'[X]

Quindi abbiamo spiegato

 qi:X,{   ,:):N{N\f{1$%!*}W$.*:+}%1$W%.*:+N,/+}/W\+_1,*X=\_W%.*:+-Y/

1]N=1

1]qi:X,1>{ ... }/

X=0un'[-1 1]0[X=0]X!+1e|

un'1N=0

]qi:X,{ ... /+}/

ovviamente dà divisione per zero. Ma se ci proviamo

]qi:X,{1e| ... /+}/

allora funziona. Noi abbiamo

             e# Stack: [] 0
1e|          e# Stack: [] 1
,:):N        e# Stack: [] [1]
{            e# We only execute this loop once
  N\f{1$%!*} e#   1 divides 1, so stack: [] [1]
  W$.*       e#   Remember: if the two arrays supplied to the pointwise operation
             e#   are not the same length then the values from the longer one are
             e#   left untouched. Stack: [] [1]
  :+         e#   Fold over a singleton. Stack: [] 1
}%           e# And that was a map, so stack: [] [1]
1$W%.*:+     e# Another [1] [] .*:+, giving the same result: 1
N,/          e# 1 / 1 = 1
+            e# And we append 1 to a giving [1]

che produce esattamente il valore richiesto.

X=0-1[-1](-1-12(-1×-1))=-101-11z


1

Pyth, 35 byte

l{m`SmSSMcXdUQk2.pQusm+L,dhHGhHtQ]Y

Dimostrazione.

Questo programma può essere diviso in due parti: in primo luogo, generiamo tutti gli alberi possibili, quindi rimuoviamo i duplicati.

Questo codice genera gli alberi: usm+L,dhHGhHtQ]Y. Gli alberi sono rappresentati come un elenco concatenato di bordi, qualcosa del genere:

[0, 1, 0, 2, 2, 3, 1, 4]

Ogni numero rappresenta un vertice e ogni due numeri è un bordo. È costruito aggiungendo ripetutamente un bordo tra i possibili vertici già esistenti e uno appena creato, e aggiungendo questo ad ogni albero possibile dal passaggio precedente. Questo genera tutti gli alberi possibili, poiché tutti gli alberi possono essere generati aggiungendo ripetutamente un vertice e un bordo da esso all'albero esistente. Tuttavia, verranno creati alberi isomorfi.

Successivamente, per ogni albero, eseguiamo ogni possibile rietichettatura. Questo viene fatto mappando su tutte le possibili permutazioni dei vertici ( m ... .pQ) e quindi spostando l'albero dall'ordinamento standard a quell'ordinamento, con XdUQk. dè l'albero, kè la permutazione.

Quindi, separiamo i bordi in liste separate con c ... 2, ordiniamo i vertici all'interno di ogni bordo con SM, Sordiniamo i bordi all'interno dell'albero con , dando una rappresentazione cannonica di ogni albero. Questi due passaggi sono il codice mSSMcXdUQk2.pQ.

Ora, abbiamo un elenco di liste composto da ogni possibile rietichettatura di ciascun albero. Ordiniamo questi elenchi con S. Ogni due alberi isomorfi deve poter essere rietichettato nel gruppo di alberi. Usando questo fatto, convertiamo ogni elenco in una stringa con `, quindi formiamo il set di quegli elenchi con {e ne stampiamo la lunghezza con l.

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.