Risultato positivo: la persistenza non costa troppo. Si può dimostrare che ogni struttura di dati può essere resa completamente persistente con al massimo un rallentamento .O(lgn)
Prova: puoi prendere un array e renderlo persistente usando strutture di dati standard (es. Un albero binario bilanciato; vedi la fine di questa risposta per un po 'più di dettaglio). Ciò comporta un rallentamento : ogni accesso all'array impiega il tempo con la struttura dei dati persistenti, anziché il tempo per l'array non persistente. Ora prendi qualsiasi algoritmo imperativo il cui tempo di esecuzione nel modello RAM è , dove indica la quantità di memoria utilizzata. Rappresenta tutta la memoria come un grande array (con elementi) e rendila persistente usando una mappa persistente. Ogni fase dell'algoritmo imperativo comporta al massimo un rallentamento , quindi il tempo di esecuzione totale èO(lgn)O(lgn)O(1)O(f(n))nnO(lgn)O(f(n)lgn) .
Apparentemente è possibile fare un po 'meglio: apparentemente si può ridurre il fattore di rallentamento a (tempo previsto, ammortizzato), usando le tecniche nel documento Demaine citato di seguito - ma non ho familiarità con i dettagli di quel lavoro, quindi non posso garantirlo io stesso. Grazie a jbapple per questa osservazione.O(lglgn)
Risultato negativo: non è possibile evitare qualche rallentamento, per alcune strutture di dati. Per rispondere alla tua terza domanda, esistono strutture di dati in cui è noto che renderle persistenti introduce un certo rallentamento.
In particolare, considera una matrice di elementi. Senza persistenza, ogni accesso all'array richiede tempo (nel modello RAM). Con persistenza, è stato apparentemente dimostrato che non c'è modo di costruire un array persistente con la complessità peggiore di per accedere a un elemento casuale. In particolare, a quanto pare esiste un limite inferiore che mostra che le matrici completamente persistenti devono avere un tempo di accesso di . Questo limite inferiore è affermato a p.3 del seguente documento:nO(1)O(1)Ω(lglgn)
Il limite inferiore è attribuito a Mihai Patrascu, ma non vi è alcuna citazione da una fonte che fornisca i dettagli della prova di questo limite inferiore affermato.
Una ricca area di ricerca. Se prendiamo una struttura di dati o un algoritmo arbitrari, è un po 'una domanda delicata se puoi renderlo persistente al massimo con rallentamento o meno. Non conosco alcun teorema di classificazione generale. Tuttavia, ci sono molte ricerche su come rendere persistenti strutture dati specifiche, in modo efficiente.O(1)
Esiste anche una forte connessione con linguaggi di programmazione funzionali. In particolare, ogni struttura di dati che può essere implementata in modo puramente funzionale (senza mutazioni) è già una struttura di dati persistente. (Il contrario non è necessariamente il caso, ahimè.) Se vuoi strizzare gli occhi, potresti prenderlo come una sorta di teorema di classificazione parziale debole: se è implementabile in un linguaggio di programmazione puramente funzionale con gli stessi limiti di tempo come in un linguaggio imperativo, quindi esiste una struttura dati persistente con gli stessi limiti temporali di quella non persistente. Mi rendo conto che questo probabilmente non è quello che stavi cercando - è soprattutto solo una banale riformulazione della situazione.
Come creare un array persistente. Non tenterò di descrivere la costruzione di come costruire un array completamente persistente con il tempo di accesso nel caso peggiore . Tuttavia, le idee di base non sono troppo complicate, quindi riassumerò l'essenza delle idee.O(lgn)
L'idea di base è che possiamo prendere qualsiasi struttura di dati ad albero binario e renderla persistente usando una tecnica chiamata copia del percorso . Diciamo che abbiamo un albero binario e vogliamo modificare il valore in qualche foglia . Tuttavia, per persistenza, non osiamo modificare il valore in quella foglia in atto. Invece, facciamo una copia di quella foglia e modifichiamo il valore nella copia. Quindi, creiamo una copia del suo genitore e cambiamo il puntatore figlio appropriato nella copia per puntare alla nuova foglia. Continua in questo modo, clonando ciascun nodo sul percorso dalla radice alla foglia. Se vogliamo modificare una foglia in profondità , questo richiede la copia nodi.d dℓdd
Se abbiamo un albero binario bilanciato ha nodi, allora tutte le foglie hanno profondità , quindi questa operazione sull'albero binario richiede tempo . Ci sono alcuni dettagli che sto saltando: per ottenere caso peggiore, potremmo aver bisogno di riequilibrare l'albero per assicurarci che rimanga equilibrato, ma questo ci dà l'idea.O ( lg n ) O ( lg n ) O ( lg n )nO(lgn)O(lgn)O(lgn)
Puoi trovare ulteriori spiegazioni, con belle immagini, alle seguenti risorse:
Questo ti darà l'idea principale. Ci sono ulteriori dettagli di cui occuparsi, ma i dettagli non rientrano nell'ambito di questa domanda. Fortunatamente, questo è tutto roba standard e ci sono molte informazioni disponibili in letteratura su come costruire tali strutture di dati. Sentiti libero di fare una domanda separata se le risorse di cui sopra non sono sufficienti e vuoi maggiori informazioni sui dettagli della costruzione di una struttura di dati di array persistente.