Come viene modellata la complessità dell'algoritmo per i linguaggi funzionali?


38

La complessità dell'algoritmo è progettata per essere indipendente dai dettagli di livello inferiore, ma si basa su un modello imperativo, ad esempio l'accesso alla matrice e la modifica di un nodo in un albero impiega il tempo O (1). Questo non è il caso di linguaggi puramente funzionali. L'elenco di Haskell richiede tempo lineare per l'accesso. La modifica di un nodo in un albero implica la creazione di una nuova copia dell'albero.

Dovrebbe quindi esistere una modellistica alternativa della complessità dell'algoritmo per i linguaggi funzionali?


3
Questo potrebbe essere quello che stai cercando.
Aristu,

1
La tua domanda potrebbe avere una risposta qui: cs.stackexchange.com/q/18262/755 . In particolare, la complessità temporale in un linguaggio puramente funzionale differisce dalla complessità temporale in un linguaggio imperativo al massimo da un rapporto di , per alcune ipotesi adeguate sulle capacità di entrambe le lingue. O(logn)
DW

3
GHC Haskell supporta matrici e alberi mutevoli e quant'altro, che consente di accedere all'array e modificare i nodi dell'albero nel tempo O (1), utilizzando "thread di stato" (le STmonadi).
Tanner Swett,

1
@BobJarvis Depends. Un elenco è un tipo di dati astratto per te o stai considerando specificamente elenchi collegati?
Raffaello

1
Quale scopo cerchi di modellare la complessità algoritmica? Stai cercando qualcosa che sia matematicamente puro, o qualcosa che sia pratico? Per un valore pratico, dovrebbe prestare attenzione a cose come se hai o meno la memorizzazione a tua disposizione, ma da un punto di vista matematico purista, le capacità dell'implementazione non dovrebbero avere importanza.
Cort Ammon,

Risposte:


34

Se supponi che il calcolo sia un buon modello di linguaggi di programmazione funzionale, allora potresti pensare: il calcolo λ ha una nozione apparentemente semplice di complessità temporale: basta contare il numero di passi di riduzione β ( λ x . M ) N M [ N / x ] .λλβ(λx.M)NM[N/x]

Ma questa è una buona misura della complessità?

Per rispondere a questa domanda, dovremmo chiarire in primo luogo cosa intendiamo per misura della complessità. Una buona risposta è data dalla tesi di Slot e van Emde Boas : qualsiasi buona misura di complessità dovrebbe avere una relazione polinomiale con la nozione canonica di complessità temporale definita usando le macchine di Turing. In altre parole, ci dovrebbe essere un 'ragionevole' codificante Da lambda termini -calcolo a macchine di Turing, come per qualche polinomio p , è il caso che per ogni termine M di dimensione | M | : M si riduce a un valore in p ( | M |tr(.)λpM|M|M passi di riduzione β esattamente quando t r ( M ) si riduce a un valore inpassi p ( | t r ( M ) | ) di una macchina di Turing.p(|M|) βtr(M)p(|tr(M)|)

Per molto tempo, non era chiaro se ciò potesse essere ottenuto nel calcolo λ. I problemi principali sono i seguenti.

  • Esistono termini che producono forme normali (in un numero polinomiale di passaggi) di dimensioni esponenziali. Anche scrivere le forme normali richiede tempo esponenziale.
  • La strategia di riduzione scelta svolge un ruolo importante. Ad esempio esiste una famiglia di termini che riduce in un numero polinomiale di β-step paralleli (nel senso di riduzione λ ottimale ), ma la cui complessità è non elementare (che significa peggio che esponenziale).

L'articolo " Beta Reduction is Invariant, Indeed " di B. Accattoli e U. Dal Lago chiarisce il problema mostrando una codifica "ragionevole" che preserva la classe di complessità P delle funzioni temporali polinomiali, assumendo riduzioni call-by-name più a sinistra più esterne . L'intuizione chiave è che l'esplosione esponenziale può avvenire solo per ragioni "poco interessanti" che possono essere sconfitte da un'adeguata condivisione. In altre parole, la classe P è la stessa se la si definisce contando i passi della macchina di Turing o le riduzioni (più a sinistra) .β

Non sono sicuro di quale sia la situazione per altre strategie di valutazione. Non sono a conoscenza del fatto che un programma simile sia stato realizzato per la complessità dello spazio.


23

La complessità dell'algoritmo è progettata per essere indipendente dai dettagli di livello inferiore.

No, non proprio. Contiamo sempre le operazioni elementari in alcuni modelli di macchine:

  • Passaggi per macchine Turing.
  • Operazioni di base su RAM.

Probabilmente stavi pensando a tutto il business / Θ / O. Sebbene sia vero che è possibile sottrarre alcuni dettagli di implementazione con gli asintotici Landau, non si elimina l'impatto del modello di macchina. Gli algoritmi hanno tempi di esecuzione molto diversi, diciamo TM e RAM, anche se si considerano solo le classi Θ !ΩΘOΘ

Pertanto, la tua domanda ha una risposta semplice: correggi un modello di macchina e quali "operazioni" contano. Questo ti darà una misura. Se si desidera che i risultati siano comparabili agli algoritmi non funzionali, sarebbe meglio servirsi per compilare i programmi in RAM (per l'analisi dell'algoritmo) o TM (per la teoria della complessità) e analizzare il risultato. Teoremi di trasferimento possono esistere per facilitare questo processo.


Concordato. Nota a margine: Le persone non fanno spesso un sacco di errori su ciò che le operazioni sono "costante". Ad esempio, supponendo che a + b sia O(1)quando è davveroO(log ab)
Paul Draper,

3
@PaulDraper È un presupposto diverso, non necessariamente un errore. Possiamo modellare ciò che vogliamo: la domanda è se risponde a domande interessanti. Vedi anche qui .
Raffaello

sembra molto simile a "sbarazzarsi del modello di macchina"
Paul Draper,

@PaulDraper Dipende dal tipo di sentimenti che aggiungi alla parola "macchina". Vedi anche questa discussione . FWIW, il modello di RAM a costo unitario - probabilmente il modello standard nell'analisi dell'algoritmo! - è utile, altrimenti non sarebbe stato usato per decenni ormai. Tutti i limiti familiari per l'ordinamento, i tress di ricerca, ecc. Sono basati su quel modello. Ha senso perché modella modelli di computer reali purché i numeri si adattino ai registri.
Raffaello

1

Invece di formulare la tua misura di complessità in termini di qualche macchina astratta sottostante, puoi inserire i costi nelle definizioni del linguaggio stesso - questa è chiamata Cost Dynamics . Uno attribuisce un costo a ogni regola di valutazione nella lingua, in modo compositivo - cioè, il costo di un'operazione è una funzione del costo delle sue sottoespressioni. Questo approccio è più naturale per i linguaggi funzionali, ma può essere utilizzato per qualsiasi linguaggio di programmazione ben definito (ovviamente, la maggior parte dei linguaggi di programmazione non è purtroppo ben definita).


<Discussione su cosa viene eliminato un modello di macchina.> Continuiamo questa discussione in chat .
Raffaello
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.