Ricorrenze e funzioni generatrici negli algoritmi


18

La combinatoria svolge un ruolo importante nell'informatica. Utilizziamo frequentemente metodi combinatori sia in analisi che in progettazione in algoritmi. Ad esempio, un metodo per trovare un set di copertine k -vertex in un grafico potrebbe semplicemente ispezionare tutti i possibili sottoinsiemi . Mentre le funzioni binomiali crescono in modo esponenziale, se è una costante fissa, finiamo con un algoritmo temporale polinomiale mediante analisi asintotica.(nk)k

Spesso i problemi della vita reale richiedono meccanismi combinatori più complessi che possiamo definire in termini di recidive. Un esempio famoso è la sequenza di fibonacci (ingenuamente) definita come:

f(n)={1if n=10if n=0f(n1)+f(n2)otherwise

Ora calcolare il valore del esimo termine cresce esponenzialmente con questa ricorrenza, ma grazie alla programmazione dinamica, possiamo calcolare in tempo lineare. Ora, non tutte le ricorrenze si prestano a DP (fuori mano, la funzione fattoriale), ma è una proprietà potenzialmente sfruttabile quando si definisce un conteggio come una ricorrenza piuttosto che una funzione generatrice.n

La generazione di funzioni è un modo elegante per formalizzare un certo conteggio per una determinata struttura. Forse la più famosa è la funzione di generazione binomiale definita come:

(x+y)α=k=0(αk)xαkyk

Fortunatamente questo ha una soluzione a forma chiusa. Non tutte le funzioni di generazione consentono una descrizione così compatta.

Ora la mia domanda è questa: con quale frequenza vengono generate le funzioni utilizzate nella progettazione di algoritmi? È facile vedere come possono essere sfruttati per comprendere il tasso di crescita richiesto da un algoritmo tramite analisi, ma cosa possono dirci di un problema durante la creazione di un metodo per risolverlo?

Se molte volte lo stesso conteggio può essere riformulato come ricorrenza, può prestarsi alla programmazione dinamica, ma forse la stessa funzione generatrice ha una forma chiusa. Quindi non è così uniformemente tagliato.


Se la funzione di generazione fornisce una formula (ad esempio, la formula di Binet per i numeri di fibonacci) che può essere utilizzata per calcolare il numero invece di utilizzare la ricorrenza (forse in modo più efficiente), la considera una risposta?
Aryabhata,

Risposte:


11

Le funzioni di generazione sono utili quando si progettano algoritmi di conteggio. Cioè, non solo quando stai cercando il numero di oggetti con una determinata proprietà, ma anche quando stai cercando un modo per enumerare questi oggetti (e, forse, generare un algoritmo per contare gli oggetti). C'è un'ottima presentazione nel capitolo 7 di Concrete Mathematics di Ronald Graham, Donald Knuth e Oren Patashnik . Gli esempi seguenti sono tratti da questi libri (gli errori e la mancanza di chiarezza sono i miei).

Supponiamo che tu stia cercando i modi per apportare modifiche con un determinato set di monete. Ad esempio, con comuni denominazioni statunitensi¹, le monete possibili sono . Per dare ¢ 42 in cambio, una possibilità è [ 25 ] [ 10 ] [ 5 ] [ 1 ] [ 1 ] ; un'altra possibilità è [ 10 ] [ 10 ] [ 10[1],[5],[10],[25],[100][25][10][5][1][1] . Scriveremo 42 [ 25 ] [ 10 ] [ 5 ] [ 1 ] 2= [ 10 ] 4 [ 1 ] 2 . Più in generale, possiamo scrivere una funzione generatrice per tutti i modi per dare il cambiamento: H = h 0 q 0 d 0 [10][10][10][10][1][1]42[25][10][5][1]2=[10]4[1]2 In termini più tecnici,Hè un termine nello spazio delle serie di potenze rispetto alle cinque variabili[100],[25],[10],[5],[1]. Definire la valutazione di un monomio in questo spazio con

H=h0q0d0n0p0[100]h[25]q[10]d[5]n[1]p
H[100],[25],[10],[5],[1] Poi i modi per dare v centesimi nel cambiamento sono il numero di monomi la cui valutazione è v . Possiamo esprimere H in modo incrementale, scrivendo prima i modi P per dare il cambiamento solo in penny, poi i modi N
[100]h[25]q[10]d[5]n[1]p=100h+25q+10d+5n+p
vvHPNper dare il resto in penny e nickel e così via. ( significa nessuna moneta.) P = I + [ 1 ] + [ 1 ] 2 + [ 1 ] 3 + = II Se vuoi contare e non solo elencare i modi per dare il cambiamento, allora c'è un modo semplice per usare le serie formali che abbiamo ottenuto. Applicare l'omomorfismo S:
P=I+[1]+[1]2+[1]3+=II[1]N=(I+[5]+[5]2+[5]3+)P=PI[5]D=(I+[10]+[10]2+[10]3+)N=NI[10]Q=(I+[25]+[25]2+[25]3+)D=DI[25]H=(I+[100]+[100]2+[100]3+)Q=QI[100]
Il coefficiente di X v in S ( C ) è il numero di modi per dare v centesimi in variazione.
S:[1]X,[5]X5,[10]X10,[25]X25,[100]X100
XvS(C)v

2×n3×n

{U=o+LV+ΓΛ+UV=IU+=VΛ=IU+=Λ
oLI=X3×(2n/3)X3k6k3k2k

Ancora una volta, leggi Concrete Mathematics per una presentazione meno affrettata³.

¹ So che la mia lista è incompleta; supponiamo un US semplificato adatto per esempi matematici.
² ² Inoltre, se si presenta, assumi monete sferiche.
³ E migliore composizione.


8

Ricordo un problema che ho dovuto risolvere durante un concorso di programmazione per studenti nel 2001. Il problema era questo:

Date le masse di 1, 7, 13, ... (Non ricordo quali masse, ma c'era un insieme finito e determinato di masse), progettare una funzione che determina se un determinato peso può essere pesato su una bilancia con questo insieme di masse.

Ho iniziato con anelli nidificati, ma ho colpito rapidamente un muro. Poi ho capito che dovevo iniziare con l'enumerazione di ciò che si può fare con le masse più leggere prima di procedere con quelle più pesanti. Potrei risolvere il problema con molti loop non previsti.

Se non fossi stato giovane arrogante e autosufficiente in quel momento (e avessi saputo e praticato le funzioni di generazione), avrei potuto definire il problema con le funzioni di generazione in quanto tali:

f(x)n

Quale peso sul piatto giusto posso pesare con una singola massa di 1?

Tre possibilità:

  • Se metto la massa sul piatto sinistro, posso pesare 1.
  • Se metto la massa sul piatto giusto, posso pesare -1.
  • Se non uso la massa, posso pesare 0.

-101X-1+1+X

1-X3X(1-X)

mX-m+1+Xm

1-X3mXm(1-Xm)

Mf

f(X)=ΠmM(1-X3m)XΣmMmΠmM(1-Xm)

Ora, dato un pacchetto in grado di eseguire operazioni sui polinomi, devi solo:

  • Calcola entrambi i prodotti.
  • Eseguire la divisione di questi prodotti, iniziando dal grado più basso. (che termina)
  • XK

w0wM

Ho progettato l'algoritmo utilizzando componenti matematicamente validi. La parte principale dell'algoritmo, che è una divisione polinomiale con il grado più basso per primo, è lineare e può essere implementata da un pacchetto standard. Potrebbe non essere ottimale, ma sicuramente si comporta meglio di quello che ho fatto al concorso e in modo meno soggetto a errori.

Se osservi attentamente il processo di divisione, vedi rapidamente che il resto può essere visto come lo "stato nascosto attuale" in ogni stato del processo e il quoziente come risultato. Il processo termina quando lo "stato nascosto attuale" raggiunge lo zero ovunque.

È possibile implementare i polinomi come array o, se sono veramente scarsi, come elenchi ordinati per coefficienti di indice e questo non cambierà l'algoritmo.


3

γ+1(m)=(2m)γ(m)+(m+1)γ1(m),γ0(m)=1,γm+1(m)=e.
γ(m)=m(γ(m1)γ1(m1)), we reduced the problem to computing some universal sequence γ(0). The latter was accomplished using generating functions, and from there we got an explicit formula for γ(m), again using generating functions. You can find the solution in the paper if you're curious, though we never bothered to include this derivation.

0

Perhaps the extensive study of Quicksort, and its many variants, is the clearest example. There combinatoric considerations governed the consideration of alternatives, and analyzing the solutions to quite complex equations shows performance advantages (or not) of them.

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.