Tempo ammortizzato costante


Risposte:


776

Tempo ammortizzato spiegato in termini semplici:

Se esegui un'operazione per un milione di volte, non ti interessa davvero il caso peggiore o il caso migliore di quell'operazione - ciò che ti interessa è quanto tempo viene impiegato in totale quando ripeti l'operazione un milione di volte .

Quindi non importa se l'operazione è molto lenta una volta ogni tanto, purché "una volta ogni tanto" sia abbastanza raro da diluire la lentezza. Il tempo essenzialmente ammortizzato significa "tempo medio impiegato per operazione, se si eseguono molte operazioni". Il tempo ammortizzato non deve essere costante; puoi avere tempo ammortizzato lineare e logaritmico o qualsiasi altra cosa.

Facciamo l'esempio di mats di un array dinamico, al quale aggiungete ripetutamente nuovi elementi. Normalmente l'aggiunta di un articolo richiede tempo costante (ovvero, O(1)). Ma ogni volta che l'array è pieno, si alloca il doppio dello spazio, si copiano i dati nella nuova area e si libera il vecchio spazio. Supponendo che le allocazioni e le liberazioni vengano eseguite in tempo costante, questo processo di ingrandimento richiede O(n)tempo in cui n è la dimensione corrente dell'array.

Quindi ogni volta che ingrandisci, impieghi circa il doppio dell'ultimo ingrandimento. Ma hai anche aspettato il doppio prima di farlo! Il costo di ciascun allargamento può quindi essere "suddiviso" tra gli inserimenti. Ciò significa che a lungo termine è il tempo totale impiegato per aggiungere m elementi all'array O(m), e quindi il tempo ammortizzato (ovvero il tempo per inserimento) è O(1).


61
Solo una nota in termini di notazione: un tempo di esecuzione costante ammortizzato di O (n) è spesso scritto come O (n) +, al contrario di solo O (n). L'aggiunta del segno più indica che il tempo di esecuzione non è garantito come O (n) e può effettivamente superare tale tempo di esecuzione.
Jeffpowrs,

1
In termini di allocazione dello spazio, viene dal mucchio?
committedandroider

3
Non sono d'accordo con "non ti importa davvero del caso peggiore". Dipende dal caso d'uso. Se alla fine sei interessato solo al risultato delle citate operazioni da 1 milione, in realtà non ti interessa. Ma se si tratta di un'app in tempo reale, che legge costantemente i dati e quindi risponde ad essi, potresti avere un grosso problema, se l'elaborazione di quei dati impiega 1 milione di volte in più del normale una volta ogni 1 milione di elementi di dati elaborati!
Kai Petzke,

2
@Jeffpowrs Ho pensato che O (n) fosse tempo lineare e O (1) fosse tempo costante . Quindi significa che O (1) + sarebbe ammortizzato tempo costante e O (n) + sarebbe ammortizzato tempo lineare ?
John Meyer,

1
@JohnMeyer Sì.
Artelius,

55

Ciò significa che, nel tempo, lo scenario peggiore verrà impostato automaticamente su O (1) o tempo costante. Un esempio comune è l'array dinamico. Se abbiamo già allocato memoria per una nuova voce, l'aggiunta sarà O (1). Se non l'abbiamo assegnato, lo faremo assegnando, diciamo, il doppio dell'importo attuale. Questo particolare inserimento non sarà O (1), ma piuttosto qualcos'altro.

Ciò che è importante è che l'algoritmo garantisce che dopo una sequenza di operazioni le operazioni costose verranno ammortizzate e quindi rendendo l'intera operazione O (1).

O in termini più rigorosi,

Esiste una costante c, tale che per ogni sequenza di operazioni (anche una che termina con un'operazione costosa) di lunghezza L, il tempo non è maggiore di c * L (Grazie Rafał Dowgird )


11
"dopo un numero sufficiente di operazioni" - il tempo ammortizzato costante non ha bisogno di questa condizione. Esiste una costante c, tale che per ogni sequenza di operazioni (anche una che termina con un'operazione costosa) di lunghezza L, il tempo non è maggiore di c * L.
Rafał Dowgird,

Da dove viene allocato il doppio dell'importo ? Non dovremmo allocare per una voce? O è un esempio ipotetico?
talekeDskobeDa

@talekeDskobaDa Questo non è un esempio arbitrario, ma un algoritmo ampiamente utilizzato. Se assegniamo spazio per una voce alla volta come suggerisci, il tempo ammortizzato per l'inserimento di un singolo valore sarebbe O (n). Se raddoppiamo lo spazio quando diventa pieno, il tempo ammortizzato è molto meglio, O (1). Per essere chiari, il problema con l'allocazione dello spazio per un elemento alla volta è che un array necessita di un grande blocco di spazio continuo. È facile ottenere un blocco più grande dal sistema operativo, ma spesso è impossibile espandere un blocco esistente perché potrebbero esserci altri dati memorizzati direttamente dopo di esso.
Artelius,

23

Per sviluppare un modo intuitivo di pensarci, considera l'inserimento di elementi in array dinamico (ad esempio std::vectorin C ++). Tracciamo un grafico che mostra la dipendenza del numero di operazioni (Y) necessarie per inserire N elementi nella matrice:

tracciare

Le parti verticali del grafico nero corrispondono alle riallocazioni della memoria per espandere una matrice. Qui possiamo vedere che questa dipendenza può essere approssimativamente rappresentata come una linea. E questa equazione di linea è Y=C*N + b( Cè costante, b= 0 nel nostro caso). Pertanto possiamo dire che dobbiamo impiegare C*Nin media operazioni per aggiungere N elementi all'array o C*1operazioni per aggiungere un elemento (tempo costante ammortizzato).


14

Di seguito ho trovato utile la spiegazione di Wikipedia, dopo aver ripetuto la lettura per 3 volte:

Fonte: https://en.wikipedia.org/wiki/Amortized_analysis#Dynamic_Array

"Matrice dinamica

Analisi ammortizzata dell'operazione Push per un array dinamico

Prendi in considerazione un array dinamico che aumenta di dimensioni man mano che vengono aggiunti più elementi, ad esempio un ArrayList in Java. Se iniziassimo con un array dinamico di dimensioni 4, ci vorrebbe tempo costante per spingere quattro elementi su di esso. Tuttavia, la spinta di un quinto elemento su quell'array richiederebbe più tempo poiché l'array dovrebbe creare un nuovo array di dimensioni doppie (8), copiare i vecchi elementi sul nuovo array e quindi aggiungere il nuovo elemento. Le successive tre operazioni di push richiederebbero allo stesso modo un tempo costante e quindi l'aggiunta successiva richiederebbe un altro lento raddoppio della dimensione dell'array.

In generale, se consideriamo un numero arbitrario di push n in un array di dimensioni n, notiamo che le operazioni di push richiedono tempo costante, tranne per l'ultimo che impiega O (n) tempo per eseguire l'operazione di raddoppio delle dimensioni. Dato che c'erano n operazioni totali, possiamo prendere la media di questo e scoprire che per spingere gli elementi sull'array dinamico sono necessari: O (n / n) = O (1), tempo costante. "

Per la mia comprensione come una semplice storia:

Supponi di avere molti soldi. E vuoi impilarli in una stanza. E hai le mani e le gambe lunghe, per tutto il tempo di cui hai bisogno ora o in futuro. E devi riempire tutto in una stanza, quindi è facile bloccarlo.

Quindi, vai dritto alla fine / angolo della stanza e inizi a impilarli. Mentre li impili, lentamente la stanza si esaurirà lo spazio. Tuttavia, durante il riempimento è stato facile impilarli. Ho i soldi, metti i soldi. Facile. È O (1). Non abbiamo bisogno di spostare alcun denaro precedente.

Una volta che la stanza esaurisce lo spazio. Abbiamo bisogno di un'altra stanza, che è più grande. Qui c'è un problema, dato che possiamo avere solo 1 stanza, quindi possiamo avere solo 1 serratura, dobbiamo spostare tutti i soldi esistenti in quella stanza nella nuova stanza più grande. Quindi, sposta tutti i soldi, dalla piccola stanza alla stanza più grande. Cioè, impilali di nuovo tutti. Quindi, dobbiamo spostare tutti i soldi precedenti. Quindi è O (N). (supponendo che N sia il conteggio totale dei soldi dei soldi precedenti)

In altre parole, è stato facile fino a N, solo 1 operazione, ma quando abbiamo bisogno di spostarci in una stanza più grande, abbiamo fatto N operazioni. Quindi, in altre parole, se calcoliamo la media, è 1 inserto all'inizio e 1 altro spostamento mentre ci si sposta in un'altra stanza. Totale di 2 operazioni, un inserto, una mossa.

Supponendo che N sia grande come 1 milione anche nella piccola stanza, le 2 operazioni rispetto a N (1 milione) non sono realmente un numero comparabile, quindi sono considerate costanti o O (1).

Supponendo che facciamo quanto sopra in un'altra stanza più grande, e di nuovo abbiamo bisogno di muoverci. È sempre lo stesso. diciamo, N2 (diciamo, 1 miliardo) è la nuova quantità di conteggio del denaro nella stanza più grande

Quindi, abbiamo N2 (che include N del precedente poiché passiamo da una stanza piccola a una più grande)

Servono ancora solo 2 operazioni, una è inserita in una stanza più grande, quindi un'altra operazione di spostamento per spostarsi in una stanza ancora più grande.

Quindi, anche per N2 (1 miliardo), sono 2 operazioni per ciascuno. che non è più niente. Quindi, è costante o O (1)

Quindi, poiché N aumenta da N a N2 o altro, non importa molto. È ancora costante, o O (1) operazioni richieste per ciascuna delle N.


Ora supponi, hai N come 1, molto piccolo, il conteggio dei soldi è piccolo e hai una stanza molto piccola, che si adatta solo a 1 conteggio dei soldi.

Non appena si riempiono i soldi nella stanza, la stanza si riempie.

Quando vai nella stanza più grande, supponi che possa contenere solo un altro denaro, per un totale di 2 conteggi di denaro. Ciò significa che il precedente ha spostato denaro e 1 altro. E ancora è pieno.

In questo modo, la N sta crescendo lentamente, e non è più costante O (1), poiché stiamo spostando tutti i soldi dalla stanza precedente, ma può contenere solo 1 altro denaro.

Dopo 100 volte, la nuova stanza può contenere fino a 100 conteggi dei soldi precedenti e 1 in più. Questo è O (N), poiché O (N + 1) è O (N), cioè il grado di 100 o 101 è lo stesso, entrambi sono centinaia, al contrario della storia precedente di, uno a milioni e uno a miliardi .

Quindi, questo è un modo inefficiente di allocare stanze (o memoria / RAM) per i nostri soldi (variabili).


Quindi, un buon modo è assegnare più spazio, con poteri di 2.

1a camera = misura 1 conteggio
2a camera = misura 4 conteggio
3a camera = misura 8 conteggio
4a camera = misura 16 conteggio
5a camera = misura 32 conteggio
6a camera = misura 64 Numero di denaro
dimensione 7 camera = attacchi 128 conteggio di denaro
dimensione 8 camera = attacchi 256 conteggio di denaro
9 superficie della camera = attacchi 512 conteggio di denaro
dimensione 10th ambiente = adatta 1024 conteggio di denaro
dimensione 11th ambiente = adatte 2.048 conteggio di denaro
. ..
16a dimensione della camera = misura 65.536 conteggio dei soldi
...
32a dimensione della camera = misura 4.294.967.296 conteggio dei soldi
...
64a dimensione della stanza = misura 18.446.744.073.709.551.616 conteggio dei soldi

Perché è meglio? Perché sembra crescere lentamente all'inizio, e più velocemente dopo, cioè rispetto alla quantità di memoria nella nostra RAM.

Questo è utile perché, nel primo caso, sebbene sia buono, la quantità totale di lavoro da fare per denaro è fissa (2) e non paragonabile alla dimensione della stanza (N), potrebbe essere anche la stanza che abbiamo occupato nelle fasi iniziali grande (1 milione) che potremmo non usare completamente a seconda che potremmo ottenere così tanti soldi da risparmiare nel primo caso.

Tuttavia, nell'ultimo caso, potenze di 2, cresce nei limiti della nostra RAM. E così, aumentando di potenze di 2, sia l'analisi Armotizzata rimane costante che è amichevole per la RAM limitata che abbiamo oggi.


2
Ah, quindi è O (peggiore / numero di operazioni). Mi piace questa risposta al meglio.
nucleartide,

1

Le spiegazioni sopra si applicano all'analisi aggregata, l'idea di prendere "una media" su più operazioni. Non sono sicuro di come si applichino al metodo dei banchieri o ai metodi di analisi ammortizzata dei fisici.

Adesso. Non sono esattamente sicuro della risposta corretta. Ma avrebbe a che fare con la condizione principale di entrambi i fisici + metodi del banchiere:

(Somma del costo ammortizzato delle operazioni)> = (Somma del costo effettivo delle operazioni).

La principale difficoltà che devo affrontare è che, dato che i costi delle operazioni ammortizzati asintotici differiscono dal costo normale asintotico, non sono sicuro di come valutare l'importanza dei costi ammortizzati.

Questo è quando qualcuno mi dà un costo ammortizzato, so che non è uguale al costo normale-asintotico Quali conclusioni devo trarre dal costo ammortizzato allora?

Dal momento che abbiamo il caso in cui alcune operazioni vengano sovraccaricate mentre altre sono sotto carico, un'ipotesi potrebbe essere che la quotazione dei costi ammortizzati delle singole operazioni non avrebbe senso.

Ad esempio: per un heap di fibonacci, la quotazione del costo ammortizzato della chiave decrescente su O (1) è insignificante poiché i costi sono ridotti del "lavoro svolto da operazioni precedenti per aumentare il potenziale dell'heap".

O

Potremmo avere un'altra ipotesi che ragiona sui costi ammortizzati come segue:

  1. So che l'operazione costosa sarà preceduta da operazioni LOW-COST MULTIPLE.

  2. Per motivi di analisi, ho intenzione di sovraccaricare alcune operazioni a basso costo, TALE CHE IL LORO COSTO ASIMPOTICO NON CAMBIA.

  3. Con queste maggiori operazioni a basso costo, posso PROVARE CHE IL FUNZIONAMENTO AMPIO ha un COSTO ASIMPOTICO PIÙ PICCOLO.

  4. Così ho migliorato / diminuito l'ASYMPTOTIC-BOUND del costo di n operazioni.

Pertanto l'analisi del costo ammortizzato + i limiti del costo ammortizzato sono ora applicabili solo alle operazioni costose. Le operazioni economiche hanno lo stesso costo ammortizzato asintotico del loro costo normale asintotico.


Pensieri interessanti.
Lonnie Best

0

È possibile calcolare la media delle prestazioni di qualsiasi funzione dividendo il "numero totale di chiamate di funzione" nel "tempo totale impiegato per tutte quelle chiamate effettuate". Anche le funzioni che richiedono sempre più tempo per ogni chiamata effettuata possono essere calcolate in media in questo modo.

Quindi, l'essenza di una funzione che esegue Constant Amortized Timeè che questo "tempo medio" raggiunge un limite che non viene superato quando il numero di chiamate continua ad aumentare. Ogni particolare chiamata può variare in termini di prestazioni, ma a lungo termine questo tempo medio non continuerà a crescere sempre più.

Questo è il merito essenziale di qualcosa che si esibisce in Constant Amortized Time.

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.