Organizzare le bolle


26

Nota, sfida copiata dalla domanda posta a math.stackexchange .

Di recente, ho raggiunto una certa abilità nel soffiare bolle. All'inizio soffiare bolle in questo modo: inserisci qui la descrizione dell'immagine

Ma poi le cose hanno iniziato a diventare strane:

inserisci qui la descrizione dell'immagine

Dopo un po 'soffiavo alcune bolle piuttosto strane:

inserisci qui la descrizione dell'immagine

Dopo aver soffiato centinaia, forse persino migliaia di tali bolle, la mia fronte improvvisamente si corrugò con la domanda: date n bolle, in quanti modi diversi è possibile sistemarle? Ad esempio, se n = 1, esiste solo 1 disposizione. Se n = 2, ci sono 2 arrangiamenti. Se n = 3, ci sono 4 arrangiamenti. Se n = 4, ci sono 9 arrangiamenti.

Ecco le 9 disposizioni di 4 bolle:
inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Dopo aver soffiato tutte queste meravigliose bolle, ho deciso che avrei dovuto condividere il piacere di contare i loro accordi con te. Quindi ecco il tuo compito:


Obbiettivo

Scrivi un programma, una funzione o simile che conta il numero di modi in cui puoi disporre le nbolle.


Ingresso

n, il numero di bolle. n> 0


Produzione

Il numero di modi in cui puoi disporre queste bolle.


Criteri vincenti

Sarebbe davvero bello se riuscissimo a far esplodere una bolla attorno al tuo codice. Più piccolo renderai il tuo codice, più facile sarà farlo. Quindi la persona che crea il codice con il minor numero di byte vincerà il concorso.


Ulteriori informazioni

OEIS


5
Se le bolle possono intersecarsi è un problema aperto , con 173 soluzioni per n = 4 .
orlp,

@orlp Fortunatamente, queste bolle non si intersecano.
TheNumberOne

1
È 0un input valido?
Martin Ender,

@ KenY-N Sì. Esiste già un link OEIS in fondo
Roman Gräf,

Oops! Elimina tempo stupido commento ...
Ken YN

Risposte:


12

Python 2, 92 87 byte

a=lambda n:n<2or sum((k%d<1)*d*a(d)*a(n-k)for k in range(1,n)for d in range(1,1+k))/~-n

In parole povere: per calcolare a(n)calcoliamo d*a(d)*a(n-k)per ogni divisore ddi ogni intero positivo kminore o uguale a n, sommiamo tutti questi e dividiamo per n-1.

Per farlo funzionare più velocemente, esegui in Python 3 (sostituendolo /con //nella funzione sopra) e memoize:

import functools
a = functools.lru_cache(None)(a)

Se lo fai, calcola a(50) = 425976989835141038353istantaneamente.


Wow, è fantastico. Presumo che lru_cache()memorizzi la funzione?
Patrick Roberts,

@PatrickRoberts Sì, di solito è usato come decoratore di funzioni, ma puoi anche applicarlo manualmente a una funzione.
orlp,

@PatrickRoberts Ecco i documenti perlru_cache .
PM 2Ring

Questa funzione ritorna Trueper n<2. Immagino che vada bene n=1, poiché in Python restituisce True1 in contesti numerici, ma a(0)dovrebbe restituire 0. Potresti risolverlo con n<2 and n or sum...ma potrebbe esserci un modo più compatto.
PM 2Ring

Immagino che si possa sostenere che esiste un modo per organizzare zero bolle, ma ciò non è coerente con A000081. OTOH, se dobbiamo solo risolvere positivamente, nallora possiamo tranquillamente ignorare questo caso d'angolo, dal momento che non influisce sulle richieste ricorsive per un livello superiore n.
PM 2Ring

10

GNU Prolog, 98 byte

b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).

Questa risposta è un ottimo esempio di come Prolog può lottare anche con i formati I / O più semplici. Funziona nel vero stile Prolog descrivendo il problema, piuttosto che l'algoritmo per risolverlo: specifica ciò che conta come una disposizione legale della bolla, chiede a Prolog di generare tutte quelle disposizioni della bolla, e quindi conta. La generazione richiede 55 caratteri (le prime due righe del programma). Il conteggio e l'I / O prendono le altre 43 (la terza riga e la nuova riga che separa le due parti). Scommetto che questo non è un problema che l'OP si aspettava che le lingue dovessero lottare con l'I / O! (Nota: l'evidenziazione della sintassi di Stack Exchange rende questa lettura più difficile, non più semplice, quindi l'ho disattivata).

Spiegazione

Iniziamo con una versione pseudocodice di un programma simile che in realtà non funziona:

b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
                and sum(BubbleCounts,InteriorCount)
                and Count is InteriorCount + 1
                and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

Dovrebbe essere abbastanza chiaro come bfunziona: stiamo rappresentando le bolle tramite elenchi ordinati (che sono una semplice implementazione di più insiemi che fa sì che uguali più insiemi siano confrontati uguali) e una singola bolla []ha un conteggio di 1, con una bolla più grande con un conteggio uguale al conteggio totale delle bolle all'interno più 1. Per un conteggio di 4, questo programma (se funzionasse) genererebbe i seguenti elenchi:

[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]

Questo programma non è adatto come risposta per diversi motivi, ma il più urgente è che Prolog in realtà non ha un mappredicato (e scriverlo richiederebbe troppi byte). Quindi, invece, scriviamo il programma in questo modo:

b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
                    and b(Tail,TailCount)
                    and Count is HeadCount + TailCount + 1
                    and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

L'altro problema principale qui è che andrà in un ciclo infinito quando eseguito, a causa del modo in cui funziona l'ordine di valutazione di Prolog. Tuttavia, possiamo risolvere il ciclo infinito riorganizzando leggermente il programma:

b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
                    and b(Head,HeadCount)
                    and b(Tail,TailCount)
                    and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

Questo potrebbe sembrare abbastanza strano - stiamo sommando i conteggi prima di sapere cosa sono - ma GNU Prolog #=è in grado di gestire quel tipo di aritmetica non causale, e perché è la prima riga di b, e HeadCounte TailCountdeve essere inferiore a Count(che è noto), serve come metodo per limitare naturalmente quante volte il termine ricorsivo può corrispondere, e quindi fa sì che il programma termini sempre.

Il prossimo passo è giocare un po 'verso il basso. Rimozione di spazi bianchi, utilizzo di nomi di variabili a carattere singolo, utilizzo di abbreviazioni come :-for ife ,for and, utilizzo setofpiuttosto che listof(ha un nome più corto e produce gli stessi risultati in questo caso) e utilizzo sort0(X,X)piuttosto che is_sorted(X)(perché is_sortednon è in realtà una funzione reale, L'ho inventato):

b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).

Questo è abbastanza breve, ma è possibile fare di meglio. L'intuizione chiave è che [H|T]è davvero prolisso man mano che vanno le sintassi dell'elenco. Come i programmatori di Lisp sapranno, un elenco è fondamentalmente solo composto da celle contro, che sono fondamentalmente solo tuple, e quasi nessuna parte di questo programma utilizza i builtin dell'elenco. Prolog ha diverse sintassi di tupla molto brevi (la mia preferita è A-B, ma la mia seconda preferita è A/B, che sto usando qui perché produce un output di debug più facile da leggere in questo caso); e possiamo anche scegliere il nostro carattere singolo nilper la fine della lista, piuttosto che rimanere bloccati con il carattere due [](ho scelto x, ma praticamente tutto funziona). Quindi, invece di [H|T], possiamo usare T/He ottenere output dab assomiglia a questo (nota che l'ordinamento su tuple è leggermente diverso da quello sugli elenchi, quindi questi non sono nello stesso ordine di sopra):

x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))

Questo è piuttosto difficile da leggere rispetto agli elenchi nidificati sopra, ma è possibile; salta mentalmente la xs e interpretala /()come una bolla (o semplicemente /come una bolla degenerata senza contenuto, se non c'è ()dopo) e gli elementi hanno una corrispondenza 1: 1 (se disordinata) con la versione dell'elenco mostrata sopra .

Naturalmente, questa rappresentazione dell'elenco, nonostante sia molto più breve, presenta un grosso svantaggio; non è integrato nella lingua, quindi non possiamo usare sort0per verificare se il nostro elenco è ordinato. sort0è comunque abbastanza prolisso, quindi farlo a mano non è una perdita enorme (in effetti, farlo a mano nella [H|T]rappresentazione dell'elenco arriva esattamente allo stesso numero di byte). L'intuizione chiave qui è che il programma come scritto controlla per vedere se l'elenco è ordinato, se la sua coda è ordinata, se la sua coda è ordinata e così via; ci sono molti controlli ridondanti e possiamo sfruttarlo. Invece, verificheremo solo che i primi due elementi siano in ordine (il che assicura che l'elenco finirà per essere ordinato una volta che l'elenco stesso e tutti i suoi suffissi sono stati controllati).

Il primo elemento è facilmente accessibile; questo è solo il capo della lista H. Il secondo elemento è piuttosto difficile da accedere, tuttavia, e potrebbe non esistere. Fortunatamente, xè meno di tutte le tuple che stiamo prendendo in considerazione (tramite l'operatore di confronto generalizzato di Prolog @>=), quindi possiamo considerare il "secondo elemento" di una lista singleton xe il programma funzionerà bene. Per quanto riguarda effettivamente l'accesso al secondo elemento, il metodo più difficile è quello di aggiungere un terzo argomento (un argomento out) a b, che ritorna xnel caso base e Hnel caso ricorsivo; questo significa che possiamo afferrare la testa della coda come output dalla seconda chiamata ricorsiva a B, e naturalmente la testa della coda è il secondo elemento della lista. Quindi bsembra così ora:

b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.

Il caso base è abbastanza semplice (elenco vuoto, restituisce un conteggio di 0, il "primo elemento" dell'elenco vuoto è x). Il caso ricorsivo inizia allo stesso modo di prima (solo con la T/Hnotazione anziché [H|T], e Hcome argomento extra); ignoriamo l'argomento aggiuntivo dalla chiamata ricorsiva sulla testa, ma lo memorizziamo nella Jchiamata ricorsiva sulla coda. Quindi tutto ciò che dobbiamo fare è accertarci che Hsia maggiore o uguale a J(ovvero "se l'elenco ha almeno due elementi, il primo è maggiore o uguale al secondo) per garantire che l'elenco venga ordinato.

Sfortunatamente, si setofadatta se si tenta di utilizzare la definizione precedente di cinsieme a questa nuova definizione di b, poiché tratta i parametri non utilizzati più o meno allo stesso modo di un SQL GROUP BY, che non è completamente quello che vogliamo. È possibile riconfigurarlo per fare ciò che vogliamo, ma quella riconfigurazione costa caratteri. Invece, usiamo findall, che ha un comportamento predefinito più conveniente ed è più lungo di soli due caratteri, dandoci questa definizione di c:

c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).

E questo è il programma completo; generare in tergo modelli di bolle, quindi spendere un intero carico di byte contandoli (abbiamo bisogno di un tempo piuttosto lungo findallper convertire il generatore in un elenco, quindi sfortunatamente un nome verbalmente lengthper controllare la lunghezza di tale elenco, oltre alla piastra di caldaia per una dichiarazione di funzione).


"Prolog in realtà non ha un predicato della mappa" : Prolog ha il maplist/2-8predicato , anche se non sono sicuro che questo renderà le cose più brevi qui.
Fatalizza il

@Fatalize: Huh, sembra che sia stato aggiunto in una versione più recente. Non è nella documentazione per l'installazione che ho e non funziona in pratica:| ?- maplist(reverse,[A,B]). uncaught exception: error(existence_error(procedure,maplist/2),top_level/0)

È davvero strano; maplistè un predicato molto comunemente usato fornito nelle principali distribuzioni Prolog (come SWI-Prolog e SiCStus)
Fatalizza il

10

Mathematica, 68 byte

Scommetto che questo può essere battuto (anche in Mathematica) con un'implementazione ex novo, ma ecco la versione integrata:

<<NumericalDifferentialEquationAnalysis`
Last@ButcherTreeCount[#+1]&

ButcherTreeCountè indicizzato 0, quindi il [#+1], e restituisce un elenco di tutti i valori fino al suo argomento, quindi il Last@. Ma per il resto è solo l'integrato per questa funzione. Tuttavia, richiede il caricamento di un pacchetto, che è quello che fa la prima riga.


8
"Certo, Mathematica ha una predisposizione per questo."
orlp,
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.