Mappa di compressione

Dimensione: px
Iniziare la visualizzazioe della pagina:

Download "Mappa di compressione"

Transcript

1 Mappa di compressione Anche avendo a disposizione un ottima funzione di hash per mappare chiavi di un determinato tipo nei corrispondenti valori k interi, resta il problema di fare in modo che questi valori siano compresi in un intervallo ben determinato, per esempio [0, N-1]. L operazione di compressione di queste chiavi rappresenta il secondo passaggio nella fase di costruzione di una funzione di hashing vera e propria. Esistono molti metodi per fare in modo di comprimere valori numerici interi in un intervallo desiderato. Il metodo della divisione Il più semplice ed immediato è quello di prendere il valore intero k della chiave e di ottenerne il modulo della divisione del valore assoluto di k per N, con N dimensione del vettore (bucket array) del dizionario: h(k) = k mod N 12

2 Questo metodo è chiamato metodo della divisione ed è il più semplice di tutti. Tuttavia bisogna prestare molta attenzione alla scelta del valore N. Infatti se si prende il valore di N troppo piccolo, per esempio N = 20 e si hanno delle chiavi uguali a 20, 40, ecc. allora si troveranno per tali valori lo stesso valore h. Così come, per fare un altro esempio, se si sceglie N = 100, chiavi appartenenti all insieme {200, 205, 210, 215,, 600} saranno mappate nel bucket array corrispondente producendo per ogni secchio tre collisioni. Si è notato come ottimi valori di N siano rappresentati dai numeri primi. Comunque, anche in questo caso non è possibile evitare delle collisioni dato che chiavi k rappresentate nella forma k = in+ j vengono mappate sempre nello stesso valore j [0, N-1], per qualsiasi valore di i ( i = 1, 2, intero). Il metodo MAD Tale metodo prende il nome dall acronimo multiply and divide (= moltiplica e dividi). Esso rappresenta un modo più sofisticato di comprimere dei valori interi in nuovi valori interi compresi nell intervallo [0, N-1]. La funzione di compressione in questo caso prende questa forma: h(k) = ak + b mod N con a e b interi non negativi tali che a non sia multiplo di N Questa funzione di compressione è stata scelta per eliminare il problema di ritrovarsi due chiavi multiple di N nella stessa casella (secchio) finale nell array. In effetti questa funzione fa in modo di ridistribuire le chiavi più uniformemente del metodo precedente. In più garantisce che la probabilità che due chiavi entrino in collisione è al più 1/N. E come se ci si trovasse nel caso di avere una funzione che ridistribuisca le chiavi a caso nel bucket array. Pur tuttavia, il problema di trovarsi ad avere a che fare con le collisioni resta, per quanto piccolo, sempre presente. Quindi, per quanto queste funzioni di hash siano ben fatte, ci sarà sempre il problema di dover gestire e risolvere le collisioni che si verificano dopo l applicazione di una di queste funzioni. Gestione delle collisioni Prima di procedere è meglio fare il punto della situazione. Per implementare un Dizionario con le tabelle di hash è necessario prendere un bucket array A[N], una funzione di hash h(k), ed utilizzarli per memorizzare in ogni casella di A[] la coppia (k,e) chiave-elemento in posizione h(k). Questa idea è molto potente nel senso che, se tutto andasse per il verso giusto, sarebbe possibile inserire, 13

3 visitare ed eliminare elementi con una efficienza sorprendenti. Tuttavia ogni tanto si verifica il problema delle collisioni, il problema cioè che partendo da due chiavi diverse k 1 e k 2, la funzione scelta per calcolare l indice della tabella di hash restituisce lo stesso valore h(k 1 ) = h(k 2 ). Esistono diverse metodologie o strategie per risolvere il problema. Catene di elementi separate Una prima soluzione consiste nel fare il secchio più grande di quello che è, cioè nel fare in modo che una singola cella del bucket array possa contenere più elementi. La cosa è possibile se si immagina che ad ogni elemento dell array A[] vengano associato un riferimento ad una seconda struttura dati S, come un vettore, una lista, o in generale un sequenza di elementi. Tale modo di gestire le collisioni è noto come metodo delle catene separate (separate chaining). Tale struttura dati secondaria può essere immaginata come un nuovo dizionario in miniatura, costruito come una sequenza non ordinata di elementi o come un log file, ma ristretto solamente agli elementi del dizionario che hanno chiavi tali da finire nella stessa casella del bucket array A[]. 14

4 Assumendo di costruire ogni mini-dizionario come un log file, è possibile realizzare i metodi fondamentali del dizionario così come mostrato: Così facendo non si fa altro che delegare ad ogni cella del vettore A[] il compito di gestire internamente possibili problemi di collisioni. Per cui ad ogni nuovo inserimento bisogna verificare il valore della chiave k, convertire tale valore in un indirizzo del vettore A[] e, una volta giunti sulla cella corrispondente, passare in rassegna tutti gli elementi della sequenza collegata a tale cella fino a trovare la prima postazione libera in cui inserire il nuovo elemento. Operazioni simili si compiono per la ricerca e la rimozione di un elemento. Naturalmente meno collisioni ci saranno nel dizionario, meno elementi ci saranno a formare le strutture dati sequenze secondarie, e quindi più veloci saranno le operazioni di inserimento, ricerca e cancellazione degli elementi. Anzi, un notevole miglioramento alla implementazione di un dizionario tramite tabelle di hash potrebbe essere fatto se venissero aggiunte strutture dati secondarie solamente in occasione di un inserimento ripetuto su una stessa casella del vettore A[]. La cosa è possibile visto che A[] viene definito come array di dati di tipo Object. Assumendo di avere una ottima funzione di hashing, allora ci si può aspettare che ogni elemento del vettore A[] memorizzi n/n elementi, nel caso n rappresenti il numero di elementi contenuto nel dizionario e, come noto, N sia la capacità del vettore A[]. Se le sequenze separate di elementi sono realizzate tramite il metodo dei file di log, allora le operazioni su tali catene saranno dell ordine di O(n/N). Quindi, per aspettarci delle prestazioni dell ordine di O(1), è necessario conoscere il numero n di elementi che saranno contenuti nel dizionario in modo da poter scegliere un valore di N opportuno. 15

5 Indirizzamento aperto (open adressing) La gestione delle collisioni tramite la costruzione di catene separate, se da un lato è comoda per il fatto di avere una semplice implementazione e di avere alcune ottime caratteristiche, dall altro costringe ad utilizzare una seconda struttura dati ausiliaria oltre al vettore A[]. In particolare, se le esigenze di spazio di memoria rappresentano una priorità, allora è possibile utilizzare gli elementi rimasti vuoti del bucket array A[] per riempirli degli elementi che hanno provocato delle collisioni. Tale metodologia non richiede dello spazio aggiuntivo di memoria, oltre a quello già allocato per l array A[], ma richiede invece una gestione delle chiavi di hashing molto più complessa del solito. Questi metodi prendono il nome di schemi ad indirizzamento aperto (open adressing shemes) e richiedono che il numero n di elementi presenti sul dizionario sia comunque minore o uguale alla dimensione N del bucket array A[]. Vi sono varie tecniche per realizzare diversi schemi di indirizzamento aperto. Linear probing (sondaggio lineare) In questo schema, il modo di procedere è molto semplice e consiste nell occupare la prima casella libera del bucket array A[] coincidente o successiva a quella recuperata dalla funzione di hashing. Per cui se la funzione di hashing h(k) ritorna come risultato un valore i, allora se A[i] rappresenta una casella già occupata, bisogna provare all indirizzo o alla casella di indirizzo A[(i+1) mod N]. Se anche questa casella è occupata, A[(i +2) mod N] e così via. Meglio fare un esempio: N=11, h(k) = k mod N, gli elementi 26, 5, 37, 16 sono già stati inseriti. Bisogna inserire l elemento di chiave k = 15. Se da un lato tale modo di procedere può sembrare facile, dall altro le cose non sono così semplici per quanto riguarda i meccanismi di rimozione di un elemento. Infatti bisogna fare in modo che tutto funzioni, bisogna fare in modo che l eliminazione di un elemento di chiave k avvenisse come se tale elemento non fosse mai stato inserito prima nel dizionario. Questo significa che bisogna traslare gli elementi dopo quello A[i] in modo tale che solamente gli elementi di chiave k vengano spostati all indietro, e gli altri lasciati stare. L esempio chiarisce meglio lo schema con cui procedere: Una soluzione a questo problema è rappresentata dall utilizzo di sentinelle per indicare che una postazione non è più piena. In questo modo un algoritmo di ricerca su un bucket array potrebbe iniziare la ricerca a partire dall indirizzo iniziale calcolato tramite la funzione di hash h(k) e passare successivamente in rassegna tutte le celle adiacenti ignorando quelle in cui sono presenti tali sentinelle fino a raggiungere l elemento trovato oppure una cella libera. Tuttavia una implementazione di questo tipo, soprattutto se si sono verificate molte 16

6 collisioni nel dizionario, complica non poco le cose e fa drasticamente calare le prestazioni dei vari algoritmi che inizialmente erano dell ordine di O(1). In particolare questo tipo di algoritmo tende a concentrare gli elementi con la stessa chiave molto vicino fra loro, complicando non poco le cose sugli inserimenti di elementi con indirizzi vicini o coincidenti a quelli già occupati. Sondaggio quadratico (quadratic probing) Un altra strategia di tipo open adressing è rappresentata dalla metodologia a sondaggio quadratico, molto simile alla precedente. La differenza sostanziale sta nella funzione di sondaggio che diventa: A[(i + f(j)) mod N], in cui f(j) = j 2 Il vantaggio di una tale soluzione consiste nel fatto che evita l accumulazione delle chiavi primarie, pur senza quasi necessitare di calcoli addizionali. Infatti il quadrato di un numero può essere espresso dalla seguente relazione di ricorrenza dove f j = j 2 e d j = 2j + 1 f j+1 = f j + d j d j+1 = d j + 2 per j > 0 Uno svantaggio di tale metodo è che non tutte le caselle dell array vengono sondate anche se rimangono delle celle vuote. Tale metodo garantisce che almeno metà dell array venga visitato se la sua dimensione N è un numero primo. Doppio hashing In questo caso la soluzione adottata consiste nell utilizzare una seconda funzione di hashing per calcolare l indirizzo della cella di memoria successiva a quella già piena. Se h(k) rappresenta la funzione di hashing utilizzata per mappare le chiavi del dizionario negli indirizzi del vettore A[], allora, se la posizione del vettore A[h(k)] è già occupata, si proverà nelle seguenti locazioni: A[(i + f(j)) mod N] per j = 1, 2, 3, fino a recuperare un posizione libera. f(j) = j h (k) con h (k) seconda funzione di hashing. Solitamente si sceglie: h (k) = q (k mod q) per qualche numero primo q < N. 17

7 Tutti questi metodi di indirizzamento aperto soffrono il grande svantaggio di complicare non poco l implementazione della struttura Dizionario, riducendo così di molto le prestazioni iniziali. Implementazione in Java di un dizionario Una volta appresi tutti i dettagli di come debbano venire costruite le hash table si passa alla realizzazione pratica di un dizionario non ordinato tramite una tabella di hash ad indirizzamento aperto con sondaggio lineare. La classe principale del codice riportato si chiama LinearProbingHashTable. I dettagli dell implementazione sono riportati di seguito: La classe mantiene delle variabili istanza n ed N per conservare rispettivamente l informazione sul numero di elementi contenuti nel dizionario e sulla capacità del bucket array A[] di supporto. Viene utilizzato un hash comparator, cioè uno strumento per comparare le chiavi dei vari elementi, e per avere una funzione di hashing h(key). Il comparatore, costruito tramite l utilizzo dell intefaccia HashComparator, fornisce i seguenti metodi: 1) iscomparable(k) per testare se una data chiave è confrontabile con altre; 2) isequalto(k1,k2) per sapere se due chiavi sono uguali; 3) hashvalue(k) che ritorna il valore dell indice del bucket array corrispondente ad una chiave k. Viene definita una sentinella AVAILABLE per segnalare il fatto che una certa locazione del bucket array è disponibile, cioè contiene elementi disattivati. Nel caso il numero di elementi n presenti sul dizionario sia pari ad N e ci sia una nuova richiesta di inserimento, allora viene generata l eccezione HashTableFullException. Vengono utilizzati i seguenti metodi ausiliari: 1) available(i) che dice se una certa posizionei del bucket array è disponibile, cioè marcata come AVAILABLE o meno. 2) Empty(i) per sapere se A[i] è vuota; 3) Key(i) per ritornare la chiave dell elemento A[i]; 4) Element(i) che ritorna l elemento contenuto in A[i]; 5) Check(k) per determinare se la chiave k è comparabile in accordo alle regole dell hash comparator, altrimenti lancia l eccezione InvalidKeyException. public class LinearProbingHashTable implements Dictionary { /** Marker for deactivated buckets */ private static Item AVAILABLE = new Item(null, null); /** number of items in the dictionary */ private int n = 0; /** capacity of the bucket array */ private int N; /** bucket array */ private Item[] A; /** hash comparator */ private HashComparator h; /** constructor providing the hash comparator */ public LinearProbingHashTable(HashComparator hc) { h = hc; N = 1023; // default capacity A = new Item[N]; } /** constructor providing the hash comparator and the capacity * of the bucket array */ 18

8 public LinearProbingHashTable(HashComparator hc, int bn) { h = hc; N = bn; A = new Item[N]; } // auxiliary methods private boolean available(int i) { return (A[i] == AVAILABLE); } private boolean empty(int i) { return (A[i] == null); } private Object key(int i) { return A[i].key(); } private Object element(int i) { return A[i].element(); } private void check(object k) { if (!h.iscomparable(k)) throw new InvalidKeyException("Invalid key."); } /** helper search method */ private int finditem(object key) throws InvalidKeyException { check(key); int i = h.hashvalue(key) % N; // division method compression map int j = i; do { if (empty(i)) return -1; // item is not found if (available(i)) i = (i + 1) % N; // bucket is deactivated else if (h.isequalto(key(i), key)) // we have found our item return i; else // we must keep looking i = (i + 1) % N; } while (i!= j); return -1; // item is not found } // methods of the dictionary ADT public Object findelement (Object key) throws InvalidKeyException { int i = finditem(key); // helper method for finding a key if (i < 0) return Dictionary.NO_SUCH_KEY; return element(i); } public void insertitem (Object key, Object element) throws InvalidKeyException { check(key); int i = h.hashvalue(key) % N; // division method compression map int j = i; // remember where we are starting do { if (empty(i) available(i)) { // this slot is available A[i] = new Item(key, element); n++; return; } i = (i + 1) % N; // check next slot } while (i!= j); // repeat until we return to start throw new HashTableFullException("Hash table is full."); } public Object removeelement (Object key) throws InvalidKeyException { int i = finditem(key); // find this key first if (i <0) return Dictionary.NO_SUCH_KEY; // nothing to remove Object toreturn = element(i); A[i] = AVAILABLE; // mark this slot as deactivated n--; return toreturn; } 19

9 Fattori di carico (load factors) e rehashing Il fattore di carico (load factor) di un dizionario in una tabella di hash è rappresentato dalla quantità di elementi n inseriti nel dizionario in rapporto alla dimensione massima N di elementi memorizzabili nel dizionario stesso. Quindi: λ = n/n. Naturalmente in tutte le tabelle viste fino ad ora si desidera che il fattore di carico di ogni tabella sia sensibilmente inferiore ad 1. Prove sperimentali hanno dimostrato che si dovrebbero mantenere fattori di carico inferiori ad ½ per tabelle di hash di tipo ad indirizzamento aperto, ed inferiori a 0.9 per tabelle di hash di tipo a catene separate. Sebbene non sia negli scopi di questo testo una analisi statistica approfondita su questi argomenti, risulta intuitivo capire il perché delle affermazioni precedenti. Infatti si può provare che già con λ 0.5 la probabilità di collisioni sia abbastanza alta. Infatti, se la funzione di hash scelta è buona, essa distribuirà gli elementi uniformemente nell intervallo [0, N-1] del bucket array di dimensione N. Quindi per memorizzare n elementi nel dizionario, il numero atteso di elementi in un singolo secchio dovrebbe essere n/n, che diventa O(1) se n è O(N). Con una implementazione a catene separate, come λ si avvicina ad 1 anche la probabilità di collisioni si avvicina ad 1, il che significa che ogni secchio del bucket array è costretto ad avere una catena di elementi separata. Le prestazioni del dizionario diventano allora quelle delle catene allegate ad ogni contenitore di elementi. Al limite, nel caso di bucket array con un solo secchio, le prestazioni dell algoritmo diventano quelle della sequenza utilizzata per gestire le collisioni. In generale la struttura assume delle prestazioni lineari O(n). Diventa quindi imperativo mantenere il fattore di carico di una hash table quanto più piccolo possibile. Alcune tecniche di implementazione delle hash table prevedono che, se il fattore di carico cresce sopra un certo valore, allora l intera tabella di hash venga rimpiazzata da una più grande. Come esempio viene riportata la tabellina seguente per fare vedere come aumenti considerevolmente la probabilità di collisioni nel caso il fattore di carico cresca oltre 0.5. L esempio è relativo ad implementazioni di tipo ad indirizzamento aperto con sondaggio quadratico (C1) e lineare (C2). Generalmente si usa raddoppiare la dimensione iniziale, ed raddoppiare successivamente la tabella ad ogni nuova necessità. Una operazione di questo tipo prevede però delle operazioni di trasferimento da una tabella all altra di tutti gli elementi inseriti e, molto di più, prevede la costruzione di una nuova funzione di hashing che permetta di mappare tutti i valori delle chiavi nel nuovo intervallo [0, 2N-1] o nei successivi intervalli [0, 2 i. N - 1], con i numero delle trasformazioni della tabella iniziale. Questo procedimento è noto col nome di rehashing. Anche con periodici riassegnamenti della tabella di hash, questa tecnica rappresenta un modo efficiente per implementare la costruzione di un dizionario non ordinato. Infatti, pensando di ammortizzare ogni nuovo raddoppio di dimensione della tabella iniziale, il costo totale delle singole operazioni di inserimento, ricerca e rimozione rimane comunque basso. Analogo discorso non è praticabile nel caso di dizionari ordinati. 20

10 L ADT Dizionario ordinato In un dizionario ordinato si vogliono compiere le comuni operazioni di un normale dizionario, ma in più si vuole mantenere anche una relazione di ordinamento fra le chiavi dei vari elementi memorizzati in esso. Per usufruire di una relazione di ordine fra le chiavi del dizionario, si utilizza lo strumento Comparatore fornito all atto della costruzione del dizionario. I metodi aggiuntivi di un dizionario ordinato sono: closestkeybefore(k) : ritorna la chiave più grande minore od uguale a k Input: Object(key) Output: Object(key) closestelembefore(k) : ritorna l elemento e per la coppia (k,e) con la chiave più grande minore od uguale a k Input: Object(key) Output: Object(elemento) closestkeyafter(k) : ritorna la chiave più piccola maggiore od uguale a k Input: Object(key) Output: Object(key) closestelementafter(k) : ritorna l elemento e per la coppia (k,e) con la chiave più piccola maggiore od uguale a k Input: Object(key) Output: Object(elemento) Tutti questi metodi ritornano l elemento speciale NO_SUCH_KEY nel caso i metodi richiamati non sappiano ritornare alcun elemento. La natura di tali metodi, strettamente connessa con le relazioni d ordinamento, fanno sì che realizzazioni pratiche di tale struttura dati attraverso l uso di log files ed hash tables siano impraticabili. Infatti le hash tables basano le loro prestazioni sul fatto che gli elementi vengono distribuiti uniformemente su una tabelle, indipendentemente dal valore delle loro chiavi. Per realizzare concretamente l ADT Dizionario Ordinato è necessario considerare nuove strutture dati dedicate a questo scopo, come le look-up tables e le skip list. Look-up Tables Se un dizionario D è ordinato, è possibile memorizzare i suoi elementi in un vettore S in ordine non decrescente delle chiavi. Si preferisce utilizzare un vettore in questo caso, piuttosto che una sequenza in generale, per il semplice motivo che il vettore permette l implementazione più veloce possibile rispetto, ad esempio, alla struttura a lista concatenata. Ci si riferirà in avanti a questa implementazione dell ADT dizionario ordinato D come look up table. Questa struttura contrasta con quella dei log files in cui viene utilizzata una sequenza non ordinata di elementi. Lo spazio richiesto per il look up table è dell ordine di Θ (n), che è simile a quella del log file, assunto che si faccia crescere o diminuire l array che supporta il vettore S in modo da mantenere la dimensione di questo array proporzionale al numero di elementi in S. 21

11 Diversamente da un log file, comunque, la gestione degli inserimenti (e delle modifiche) richiede una notevole quantità di tempo. In particolare, l inserimento di una coppia (k,e) nella struttura è dell ordine di O(n) nel caso peggiore, dato che è necessario traslare tutti gli elementi di chiave maggiore di quella dell elemento che è stato appena inserito. Quindi, per quanto riguarda le operazioni di inserimento e cancellazione le prestazioni sono comunque peggiori rispetto alla struttura di tipo log file. Tuttavia è possibile effettuare delle operazioni di ricerca dei dati in un modo molto più veloce rispetto ai log files. Ricerca binaria Un significativo vantaggio dell utilizzare le look up tables è che esse permettono di effettuare le operazioni di ricerca di un elemento con il metodo della ricerca binaria. Per questo tipo di ricerca è necessario fare riferimento, come in questo caso, ad una struttura a Vettori che utilizzino il rango come mezzo per visitare il contenuto di una cella del vettore (capitolo 5). Se gli elementi del dizionario ordinato D, costruito attraverso l utilizzo di un vettore S ordinato, allora l elemento di rango i non ha chiave più piccola di quelle contenute negli elementi di S di rango 0, 1, 2,,i-1. E neppure ha chiave più grande di quelle contenute nelle celle del vettore di rango i+1, i+2,,n-1, ove n è la dimensione del vettore. Queste osservazioni ci permettono di poter raggiungere rapidamente l elemento con chiave cercata uguale a k. 22

12 Il meccanismo di funzionamento di questo metodo di ricerca è molto semplice. Si comincia col dividere l insieme di elementi S in due sottoinsiemi formati dagli elementi di intervallo [0, i-1] e [i, n-1], ove i corrisponde circa all elemento mediano del vettore. Successivamente bisogna confrontare tale chiave con quella fornita come parametro per la ricerca dell elemento. Nel caso la chiave temporanea recuperata sia maggiore di quella fornita allora la ricerca va ripetuta allo stesso modo nel sottoinsieme di sinistra del sottoinsieme considerato, dividendo in due tale sottoinsieme, ecc. Nel caso invece la chiave recuperata sia più piccola, allora il nuovo sottoinsieme da considerare è quello di destra. La cosa si ripete fino al ritrovamento di un elemento di chiave uguale oppure fino a ritrovarsi con un sottoinsieme costituito da un solo elemento con chiave diversa da quella cercata. L algoritmo utilizza due parametri low e high. Si utilizzarà la notazione key(i) per indicar la chiave contenuta nell elemento di indice o rango i. Inizialmente low = 0 ed high =(n-1), se n è la dimensione del vettore su cui effettuare la ricerca dell elemento di chiave k. Successivamente si confronta la chiave k con quella dell elemento preso come candidato di indice: low + high mid = 2 Si considerano i tre casi: 1) se k = key(mid) allora la ricerca è terminata con esito favorevole. L elemento cercato è proprio l elemento di rango uguale a mid. 2) Se k < key(mid) allora bisogna considerare come nuovo insieme su cui effettuare la ricerca quello formato dagli elementi col rango contenuto nell intervallo [0, mid-1] 3) Se k > key(mid) allora bisogna considerare come nuovo insieme su cui effettuare la ricerca quello formato dagli elementi col rango contenuto nell intervallo [mid, n-1] La figura seguente mostra l algoritmo di ricerca binaria. Si può capire che tale metodo è di tipo ricorsivo, perché applica di volta in volta lo stesso criterio di ricerca su insiemi sempre più piccoli. Essendo ricorsivo, il metodo utilizza come parametri aggiuntivi gli indici del vettore S che delimitano il sottoinsieme di ricerca. 23

13 Per quanto riguarda le prestazioni di ricerca dell algoritmo, si fanno queste considerazioni. Inizialmente il numero di candidati per la ricerca è n. Successivamente, al secondo passo, l insieme si dimezza e diventa n/2. Al terzo passaggio l insieme rimasto è composto da n/4 elementi. In generale dopo i passaggi, l insieme su cui cercare l elemento di chiave k è composto da n/2 i elementi. Nel caso peggiore, il metodo si ferma dopo aver visitato l unico elemento che costituisce l insieme n all m-esimo passaggio. Il che significa che deve essere 1 2 <. Quindi: m m > log n per cui: m = log n + 1 che implica che l algoritmo di ricerca binaria abbia una complessità O(n). Queste considerazioni suggeriscono il fatto che una tale implementazione dell ADT dizionario ordinato è efficace per effettuare ripetute ricerche sugli elementi del vettore e non per fare molti inserimenti od eliminazioni. La tabella comparativa seguente confronta la struttura dati look-up table con quella di un log file. Skip Lists Una struttura dati interessante per realizzare efficacemente l ADT dizionario ordinato è quella delle skip list. Questa struttura dati fa delle scelte casuali per sistemare gli elementi in un modo tale che le ricerche e gli aggiornamenti su di essi avvengano in un tempo dell'ordine di O(n) nel caso medio, con n numero degli elementi inserito nel dizionario. Interessante è il concetto di complessità media temporale che non dipende dalla distribuzione di probabilità delle chiavi durante le fasi di inserimento. Invece dipende dall'utilizzo di un generatore di numeri casuali nell'implementazione del metodo per l'inserimento degli elementi per aiutare a decidere dove piazzare i nuovi elementi. Il 24

14 tempo necessario all'algoritmo è calcolato come la media di tutti i possibili casi di estrazioni numeriche che si possono verificare durante l'inserimento. Dato che i metodi per generare numeri casuali sono molto utilizzati, a partire dai videogiochi, per passare ai meccanismi di crittografia e alle simulazioni al calcolatore, i metodi per generare numeri casuali sono inseriti in quasi tutti i calcolatori. Alcuni di questi metodi, chiamati pseudo generatori di numeri casuali, generano numeri pseudo casuali in modo deterministico, a partire da un numero iniziale chiamato seme (seed). Altri metodi utilizzano le parti hardware del calcolatore per estrarre numeri che vengono considerati 'veramente' casuali. In ogni caso si può affermare che i risultati ottenuti sono sufficienti per le analisi che verranno fatte. Il più gran vantaggio dell'utilizzare meccanismi casuali nella progettazione di algoritmi e strutture dati è che i metodi e le strutture dati che ne scaturiscono sono sempre semplici ed efficienti. E' possibile escogitare una semplice struttura dati basata sulla generazione di numeri casuali, chiamata skip list, che ha gli stessi limiti temporali dell'algoritmo di ricerca binaria visto poco fa. Non dimeno questi limiti sono attesi, nel senso che essi rappresentano i limiti del caso peggiore della ricerca binaria nelle look-up tables. D'altra parte le skip list sono molto più veloci delle look-up tables per quanto riguarda le fasi di inserimento ed estrazione dei dati. Una skip list S che implementa un dizionario ordinato D consiste di una serie di liste {S 0, S 1,..., S h }. Ogni lista S i memorizza un sottoinsieme degli elementi del dizionario D le cui chiavi sono ordinate in modo non decrescente, più due elementi con chiavi speciali, denotate dai simboli + e -, dove - rappresenta il più piccolo valore che possa essere utilizzato per una chiave di un elemento in D, e +, il più grande. In più le liste in S soddisfano le seguenti regole: La lista S 0 contiene ogni elemento presente nel dizionario (più i due speciali elementi con chiavi + e - ); Per i = 1, 2,..., h-1, la lista S i contiene (oltre agli elementi di chiave - e + ) un sottoinsieme deciso a caso degli elementi contenuti nella lista S i-1 ; La lista S h contiene solamente gli elementi di chiave + e - Un esempio di una skip list è mostrato nella figura seguente: E' consuetudine visualizzare una skip list con la lista S 0 nella parte inferiore e la lista S h in quella superiore. Solitamente ci si riferisce ad h come alla altezza della skip list S. Intuitivamente le skip list sono costruite in modo tale che S i+1 contenga più o meno ogni altro elemento contenuto in S i. Come si vedrà meglio nei dettagli del metodo di inserimento degli elementi, gli elementi in S i+1 sono presi a caso da quelli della lista S i con probabilità di pescaggio uguale ad 1/2. Il che significa che, per inserire gli elementi nella lista S i+1, si parte dagli elementi 25

15 della lista S i e, per ognuno di essi, si lancia un moneta in aria per decidere se debba anch esso far parte della lista superiore S i+1. Così, se la lista S 0 riporta tutti gli n elementi del dizionario, ci si aspetta che la lista S 1 ne riporti solamente la metà, cioè n/2. E che la lista S 2 ne porti 1/4, ossia n/4, e così via. In generale la lista S i avrà n/2 i elementi. In altre parole ci si aspetta che l'altezza della skip list sia uguale all'incirca log(n). Il dimezzamento degli elementi di una lista rispetto a quelli della precedente non è un proprietà stabilità dalle regole di costruzione della struttura, bensì dalla generazione casuale dei numeri il cui valore ha stabilito se un elemento dovesse fare parte della lista superiore o meno. Utilizzando il concetto astratto di Posizione visto per le liste e gli alberi, è possibile vedere una skip list come una collezione bidimensionale di posizioni sistemate in modo orizzontale,organizzate su livelli (levels) e, verticale, organizzate in torri (towers). Ogni livello rappresenta una lista Si ed ogni torre contiene gli stessi elementi attraverso liste consecutive. Le posizioni in una skip list possono essere attraversate attraverso l'utilizzo di questi metodi: after(p): ritorna la posizione che segue p sullo stesso livello before(p): ritorna la posizione precedente a p sullo stesso livello below(p): ritorna la posizione sotto p nella stessa torre above(p): ritorna la posizione sopra p nella stessa torre Si assume in modo convenzionale che il metodo above(p) ritorni una posizione nulla nel caso non esista la posizione sopra quella precedente p. Senza scendere nei dettagli si vede come sia facile implementare una struttura a skip lists tramite l'utilizzo di strutture dati concatenate tramite riferimenti ricorsivi. E che l'utilizzo di tali riferimenti permetta di ottenere prestazioni dell'ordine di O(1) per i quattro metodi esposti. Ecco un esempio di come potrebbe essere costruito un nodo per una struttura di questo tipo: Ed ecco ancora un esempio di skip list costruito basandosi sul nodo così creato. Si faccia attenzione come i riferimenti alla lista inferiore o superiore siano nulli nel caso della lista S 0 o S h dove h rappresenta la lista con meno elementi: 26

16 Ricerca Le skip list permettono di costruire semplici algoritmi di ricerca per i Dizionari ordinati. Infatti tutti gli algoritmi di ricerca sulle skip list sono basati sull'elegante metodo di ricerca SkipSearch che prende una chiave k e trova l'elemento in una skip list S con la più larga chiave che è minore o uguale a k. Supponiamo venga data una tal chiave k. Si inizia la ricerca con l'impostare una variabile p di tipo posizione sull'elemento più in alto a sinistra nella struttura delle skip list. Cioè a p viene assegnata la posizione relativa all'elemento di chiave - della lista S h. Dopo di che si eseguono i seguenti passi: 1) Se S.below(p) è nullo, allora la ricerca ha termine. Si è giunti nella lista S 0 inferiore e non ci sono altre liste sotto. Ed è stato altresì localizzato il nodo con chiave minore uguale a quella cercata k. Altrimenti, si scende di un livello dalla torre attraverso l'assegnamento p below(p) 2) Partendo dalla posizione p, ci si muove sullo stesso livello verso destra fino a raggiungere l'elemento di posizione più a destra tale che la sua chiave sia key(p) k. Questo passo si chiama 'scan forward'. Va notato che ogni posizione esiste sempre per il fatto che sono stati introdotti gli elementi di chiavi - e + per ogni livello. Infatti dopo aver eseguito il passo 2) (scan forward) la posizione può rimanere dove era inizialmente in quel livello. In ogni caso si ripete il passo 1. 27

17 Ecco la descrizione dello pseudo codice dell'algoritmo. Una volta scritto l'algoritmo è facile cercare un qualsiasi elemento di chiave k del dizionario semplicemente richiamando il metodo attraverso questa assegnazione: p SkipSearch(k) e verificare poi se effettivamente key(p)=k si o no. Se il confronto ha esito positivo si ritornerà p, altrimenti il valore speciale NO_SUCH_KEY. Come già detto le prestazioni dell'algoritmo sono O(log n). Inserimento L inserimento di un nuovo elemento di chiave k avviene sempre partendo dall algoritmo di ricerca dell elemento con chiave k. In ogni caso l algoritmo di ricerca ritorna la posizione da cui effettuare l inserimento sulla lista S 0. Comunque è possibile inserire in S 0 l elemento desiderato attraverso il metodo insertafterabove che inserisce un nuovo elemento nella lista e successivamente ritorna la posizione corrispondente a quella ottenibile con la chiamata del metodo above(p). Una volta inserito il nuovo elemento viene fatta una scelta casuale di probabilità ½ per determinare se anche la lista superiore debba contenere o meno l elemento. Ecco il codice: 28

18 Rimozione Così come per l inserimento le operazioni di rimozione di un elemento da una skip list sono molto semplici. Naturalmente si inizia sempre dalla lista S 0 inferiore dalla posizione fornita dal metodo di ricerca dell elemento da eliminare con chiave k, metodo che necessariamente deve essere sempre invocato prima di ogni operazione di rimozione. Successivamente si eliminano gli elementi delle liste superiori fino a trovarne uno per ogni lista. Maggiori dettagli sul libro di testo. In concreto, esperimenti pratici hanno dimostrato l enorme efficacia di tali strutture, ben superiore a quella degli alberi AVL. Naturalmente si paga un costo in termini di risorse di memoria. 29

19 Mantenere il livello più elevato Ci sono due politiche di gestione delle skip list. La prima consiste nel mantenere un numero stabilito di livelli per la struttura si una skip list. Questa politica presuppone un minore spreco di risorse ma, d altro canto favorisce il decadimento delle prestazioni degli algoritmi di inserimento, ricerca e rimozione sul dizionario. La seconda consiste nel non limitare il numero di liste che potrebbero essere create dopo un certo numero di inserimenti di nuovi elementi nel dizionario, in modo da lasciare inalterate le prestazioni dei vari metodi sulla struttura, a scapito però di un prezzo più alto da pagare in termini di risorse di memoria. L unica cosa consolante è che, dati n elementi, ci si può aspettare che vi siano log n liste che, tutto considerato, non è un valore molto grande. 30

Il TDA Map. Tabelle hash

Il TDA Map. Tabelle hash Il TDA Map Tabelle hash Definizione informale Il TDA Map memorizza coppie formate da una chiave k e da un valore v La coppia è chiamata entry Ogni chiave deve essere unica Questa è la differenza principale

Dettagli

Definizione informale. Il TDA Map memorizza coppie formate da una chiave k e da un valore v. La coppia è chiamata entry. Ogni chiave deve essere unica

Definizione informale. Il TDA Map memorizza coppie formate da una chiave k e da un valore v. La coppia è chiamata entry. Ogni chiave deve essere unica Il TDA Map Tabelle hash Definizione informale Il TDA Map memorizza coppie formate da una chiave k e da un valore v La coppia è chiamata entry Ogni chiave deve essere unica Questa è la differenza principale

Dettagli

Il TDA Map. Definizione informale. I metodi del TDA Map 2. I metodi del TDA Map 1. Interfaccia Map 1 NO_SUCH_KEY. Tabelle hash

Il TDA Map. Definizione informale. I metodi del TDA Map 2. I metodi del TDA Map 1. Interfaccia Map 1 NO_SUCH_KEY. Tabelle hash Il TDA Map Tabelle hash Definizione informale Il TDA Map memorizza coppie formate da una chiave k e da un valore v La coppia è chiamata entry Ogni chiave deve essere unica Questa è la differenza principale

Dettagli

Esercizio. Scrivere una classe ListMap<K, V> che implementa la mappa con una lista. Strutture Dati

Esercizio. Scrivere una classe ListMap<K, V> che implementa la mappa con una lista. Strutture Dati Esercizio Scrivere una classe ListMap che implementa la mappa con una lista Esercizio Si scriva un metodo public static PositionList preorder(linkedbinarytree T) che preso in

Dettagli

Informatica 3. Informatica 3. LEZIONE 22: Politiche di risoluzione delle collisioni. Lezione 23 - Modulo 1. Politiche di risoluzione delle collisioni

Informatica 3. Informatica 3. LEZIONE 22: Politiche di risoluzione delle collisioni. Lezione 23 - Modulo 1. Politiche di risoluzione delle collisioni Informatica 3 Informatica 3 LEZIONE 22: Politiche di risoluzione delle collisioni Lezione 23 - Modulo 1 Modulo 1: Open e closed hashing Modulo 2: Sequenze di probe Modulo 3: Analisi del closed hashing

Dettagli

Il TDA Dictionary. Definizione informale. I metodi del TDA Dictionary 1. Applicazioni. I metodi del TDA Dictionary 2. I metodi del TDA Dictionary 3

Il TDA Dictionary. Definizione informale. I metodi del TDA Dictionary 1. Applicazioni. I metodi del TDA Dictionary 2. I metodi del TDA Dictionary 3 Il TDA Dictionary Definizione informale Il TDA Dictionary (dizionario) modella una collezione di voci su cui è possibile effettuare delle ricerche Una voce è una coppia (chiave, elemento) Le principali

Dettagli

INTRODUZIONE INTRODUZIONE TABELLE HASH FUNZIONE HASH

INTRODUZIONE INTRODUZIONE TABELLE HASH FUNZIONE HASH INTRODUZIONE INTRODUZIONE Una tabella hash è una struttura dati che permette operazioni di ricerca e inserimento molto veloci: in pratica si ha un costo computazionale costante O(1). Si ricorda che la

Dettagli

Esercizi Capitolo 7 - Hash

Esercizi Capitolo 7 - Hash Esercizi Capitolo 7 - Hash Alberto Montresor 19 Agosto, 2014 Alcuni degli esercizi che seguono sono associati alle rispettive soluzioni. Se il vostro lettore PDF lo consente, è possibile saltare alle rispettive

Dettagli

ADT Dizionario. Come nella Mappa: Diversamente dalla Mappa:

ADT Dizionario. Come nella Mappa: Diversamente dalla Mappa: Come nella Mappa: un Dizionario è un contenitore di elementi del tipo (k,v) dove k è la chiave e v è il suo corrispondente valore. ogni elemento (k,v) viene detto entrata (entry) del Dizionario. chiavi

Dettagli

Informatica 3. Informatica 3. LEZIONE 21: Ricerca su liste e tecniche di hashing. Lezione 21 - Modulo 1. Introduzione (1) Introduzione (2) Ricerca:

Informatica 3. Informatica 3. LEZIONE 21: Ricerca su liste e tecniche di hashing. Lezione 21 - Modulo 1. Introduzione (1) Introduzione (2) Ricerca: Informatica 3 Informatica 3 LEZIONE 21: Ricerca su liste e tecniche di hashing Modulo 1: Algoritmi sequenziali e basati su liste Modulo 2: Hashing Lezione 21 - Modulo 1 Algoritmi sequenziali e basati su

Dettagli

Massimo Benerecetti Tabelle Hash: gestione delle collisioni

Massimo Benerecetti Tabelle Hash: gestione delle collisioni Massimo Benerecetti Tabelle Hash: gestione delle collisioni # Lezione n. Parole chiave: Corso di Laurea: Informatica Insegnamento: Algoritmi e Strutture Dati I Email Docente: bene@na.infn.it A.A. 2009-2010

Dettagli

Hash file. idea di base : il file è organizzato in B bucket, i record sono assegnati ai bucket in ragione del valore di chiave.

Hash file. idea di base : il file è organizzato in B bucket, i record sono assegnati ai bucket in ragione del valore di chiave. Hash file idea di base : il file è organizzato in B bucket, i record sono assegnati ai bucket in ragione del valore di chiave. hash(chiave) [0,B-1] è desiderabile una distribuzione uniforme sui bucket.

Dettagli

IMPLEMENTAZIONE DI UN ALBERO AVL

IMPLEMENTAZIONE DI UN ALBERO AVL IMPLEMENTAZIONE DI UN ALBERO AVL Dedichiamoci ora all implementazione dei dettagli ed all analisi dell ADT Dizionario costruito tramite un albero di ricerca AVL. Le operazioni di inserimento e rimozione

Dettagli

Sommario. Tabelle ad indirizzamento diretto e hash Funzioni Hash

Sommario. Tabelle ad indirizzamento diretto e hash Funzioni Hash Funzioni Hash Sommario Tabelle ad indirizzamento diretto e hash Funzioni Hash Requisiti Metodo della divisione Metodo della moltiplicazione Funzione Hash Universale La ricerca Talvolta si richiede che

Dettagli

Tabelle Hash! Presentare le tabelle hash e la nozione di funzione di hashing Discutere la complessità di questa realizzazione per le Tavole

Tabelle Hash! Presentare le tabelle hash e la nozione di funzione di hashing Discutere la complessità di questa realizzazione per le Tavole Tabelle Hash! n Obiettivi: Presentare le tabelle hash e la nozione di funzione di hashing Discutere la complessità di questa realizzazione per le Tavole 1 Tavole! n Una tavola è un tipo di dato astratto

Dettagli

Alberi binari di ricerca

Alberi binari di ricerca Alberi binari di ricerca Gli alberi binari di ricerca sono ottime strutture dati per memorizzare coppie di elementi (k, e) chiave elemento di un dizionario. Un albero binario di ricerca T è un albero binario

Dettagli

Heap scenario. Ho un insieme dinamico di oggetti, ciascuno identificato con una priorità. (la priorità è semplicemente un numero);

Heap scenario. Ho un insieme dinamico di oggetti, ciascuno identificato con una priorità. (la priorità è semplicemente un numero); Heap Heap scenario Ho un insieme dinamico di oggetti, ciascuno identificato con una priorità. (la priorità è semplicemente un numero); Voglio poter: inserire: nuovi elementi, ciascuno con una data priorità

Dettagli

ADT Mappa. Le chiavi (il mezzo per accedere agli elementi) hanno lo scopo di rendere efficiente la ricerca. Strutture Dati

ADT Mappa. Le chiavi (il mezzo per accedere agli elementi) hanno lo scopo di rendere efficiente la ricerca. Strutture Dati ADT Mappa Una mappa è un contenitore di elementi del tipo (k,v) dove k è la chiave e v è il suo corrispondente valore ogni elemento (k,v) viene detto entrata (entry) della mappa entrate multiple con la

Dettagli

ADT Dizionario. Ordered search table. Supponiamo che sia definita una relazione d'ordine totale sulle chiavi del dizionario D:

ADT Dizionario. Ordered search table. Supponiamo che sia definita una relazione d'ordine totale sulle chiavi del dizionario D: Ordered search table Supponiamo che sia definita una relazione d'ordine totale sulle chiavi del dizionario D: possiamo memorizzare le entrate di D in un array list S in ordine non decrescente di chiavi:

Dettagli

Algoritmo di ordinamento sul posto che ha tempo di esecuzione :

Algoritmo di ordinamento sul posto che ha tempo di esecuzione : QuickSort Algoritmo di ordinamento sul posto che ha tempo di esecuzione : - O(n 2 ) nel caso peggiore - O(n log n) nel caso medio Nonostante le cattive prestazioni nel caso peggiore, rimane il miglior

Dettagli

Tabelle Hash. Implementazioni Dizionario. Implementazione. Tabelle ad accesso diretto. Tempo richiesto dall operazione più costosa:

Tabelle Hash. Implementazioni Dizionario. Implementazione. Tabelle ad accesso diretto. Tempo richiesto dall operazione più costosa: Algoritmi e Strutture Dati Implementazioni Dizionario Tempo richiesto dall operazione più costosa: Tabelle Hash - Liste - Alberi di ricerca non bilanciati - Alberi di ricerca bilanciati -Tabelle hash O(n)

Dettagli

Implementazione di dizionari

Implementazione di dizionari Implementazione di dizionari Problema del dizionario dinamico Scegliere una struttura dati in cui memorizzare dei record con un campo key e alcuni altri campi in cui sono memorizzati i dati associati alla

Dettagli

Esercizio. Strutture Dati

Esercizio. Strutture Dati Esercizio Si scriva un metodo public Position sibling(position v) che preso in input un nodo v restituisca il fratello di v. Il metodo dovrà lanciare una BoundaryViolationException nel caso in cui

Dettagli

Calcolare x n = x x x (n volte)

Calcolare x n = x x x (n volte) Calcolare x n = x x x (n volte) Abbiamo bisogno di: una variabile ris in cui ad ogni iterazione del ciclo si ha un risultato parziale, e che dopo l ultima iterazione contiene il risultato finale; una variabile

Dettagli

Massimo Benerecetti Tabelle Hash

Massimo Benerecetti Tabelle Hash Massimo Benerecetti Tabelle Hash # Lezione n. Parole chiave: Corso di Laurea: Informatica Insegnamento: Algoritmi e Strutture Dati I Email Docente: bene@na.infn.it A.A. 2009-2010 Rappresentazione di insiemi

Dettagli

Sommario. Le strutture dati elementari per implementare sequenze: Vettori Liste

Sommario. Le strutture dati elementari per implementare sequenze: Vettori Liste Sequenze Sommario Le strutture dati elementari per implementare sequenze: Vettori Liste Strutture dati elementari Le strutture dati vettore e lista sono fra le strutture dati più usate e semplici Il loro

Dettagli

Tavole hash. Alcune possibili implementazioni dell ADT Dizionario. Tavole ad accesso diretto. Tempo richiesto dall operazione più costosa:

Tavole hash. Alcune possibili implementazioni dell ADT Dizionario. Tavole ad accesso diretto. Tempo richiesto dall operazione più costosa: Tavole hash Una rapida panoramica Alcune possibili implementazioni dell ADT Dizionario Tempo richiesto dall operazione più costosa: Liste Alberi di ricerca non bilanciati Alberi di ricerca bilanciati Tavole

Dettagli

come segue: data una collezione C di elementi e una un elemento che verifica la proprietà P

come segue: data una collezione C di elementi e una un elemento che verifica la proprietà P Problemi di Ricerca Carla Binucci e Walter Didimo Il problema della Ricerca In generale, il Problema della Ricerca è definito come segue: data una collezione C di elementi e una proprietà P, determinare

Dettagli

Gianpiero Cabodi e Paolo Camurati Dip. Automatica e Informatica Politecnico di Torino

Gianpiero Cabodi e Paolo Camurati Dip. Automatica e Informatica Politecnico di Torino Le tabelle di hash Gianpiero Cabodi e Paolo Camurati Dip. Automatica e Informatica Politecnico di Torino Tabelle di hash Finora gli algoritmi di ricerca si erano basati sul confronto. Eccezione: tabelle

Dettagli

Quick Sort. PARTITION(A,p,r) risistema il sottoarray A[p...r] e riporta l indice q:

Quick Sort. PARTITION(A,p,r) risistema il sottoarray A[p...r] e riporta l indice q: Quick Sort - Ordinamento in loco - Tempo di esecuzione nel caso peggiore: Θ(n 2 ) - Tempo di esecuzione nel caso medio: Θ(n lg n) - I fattori costanti nascosti nella notazione Θ sono abbastanza piccoli

Dettagli

Tavole (o tabelle) hash

Tavole (o tabelle) hash Alcune possibili implementazioni dell ADT Dizionario Tavole (o tabelle) hash Una rapida panoramica (vedere anche [Deme] cap. 7) Parte 23 del Corso Algoritmi e Laboratorio a.a. 2006-07 Tempo richiesto dall

Dettagli

Tabelle Hash. Massimo Benerecetti. Informatica. Lezione n. Parole chiave: Inserire testo. Corso di Laurea:

Tabelle Hash. Massimo Benerecetti. Informatica. Lezione n. Parole chiave: Inserire testo. Corso di Laurea: Massimo Benerecetti Tabelle Hash Lezione n. Parole chiave: Inserire testo Corso di Laurea: Informatica Insegnamento: Algoritmi e Strutture Dati I Email Docente: bene@na.infn.it A.A. 2009-2010 Rappresentazione

Dettagli

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati Algoritmi e Strutture Dati Tabelle di hash Fabio Patrizi 1 Implementazioni Dizionario Tempo richiesto dall operazione più costosa: - Liste - Alberi di ricerca non bilanciati - Alberi di ricerca bilanciati

Dettagli

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati Algoritmi e Strutture Dati Tabelle di hash Domenico Fabio Savo 1 Implementazioni Dizionario Tempo richiesto dall operazione più costosa: - Liste - Alberi di ricerca non bilanciati - Alberi di ricerca bilanciati

Dettagli

LINKEDLIST: implementazione iteratore. LINKEDLIST: iteratore INNERITERATOR INNERITERATOR

LINKEDLIST: implementazione iteratore. LINKEDLIST: iteratore INNERITERATOR INNERITERATOR LINKEDLIST: iteratore Il metodo iterator() restituisce un oggetto iteratore sulla lista per scandire gli elementi in sequenza a partire dall inizio della lista. public Iterator iterator() { return new

Dettagli

Esercitazione 5 Algorithmi e Strutture Dati (Informatica) A.A 2015/2016

Esercitazione 5 Algorithmi e Strutture Dati (Informatica) A.A 2015/2016 Esercitazione 5 Algorithmi e Strutture Dati (Informatica) A.A 2015/2016 Tong Liu April 7, 2016 Liste trabocco (Separate Chaining) Esercizio 1 [Libro 7.5] Un dizionario è realizzato con liste di trabocco.

Dettagli

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati Algoritmi e Strutture Dati Capitolo 7 Tabelle hash Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Implementazioni Dizionario Tempo richiesto dall operazione più costosa: -Liste - Alberi di ricerca

Dettagli

complessità degli algoritmi

complessità degli algoritmi complessità degli algoritmi progetto CORDA informatica algoritmo matematico persiano Muhammad al-khwarizmi (IX secolo) un algoritmo è una sequenza finita di passi interpretabili da un esecutore l esecuzione

Dettagli

Sommario. Algoritmi di ordinamento lineari: CountingSort. BucketSort. RadixSort

Sommario. Algoritmi di ordinamento lineari: CountingSort. BucketSort. RadixSort Sommario Algoritmi di ordinamento lineari:! CountingSort! BucketSort! RadixSort 1 Ordinamento in tempo lineare. Il limite inferiore Ω(n log n) vale per tutti gli algoritmi di ordinamento generali, nel

Dettagli

UNIVERSITÀ DEGLI STUDI DI PAVIA FACOLTÀ DI INGEGNERIA. Algoritmi

UNIVERSITÀ DEGLI STUDI DI PAVIA FACOLTÀ DI INGEGNERIA. Algoritmi UNIVERSITÀ DEGLI STUDI DI PAVIA FACOLTÀ DI INGEGNERIA Algoritmi Algoritmi classici Alcuni problemi si presentano con elevata frequenza e sono stati ampiamente studiati Ricerca di un elemento in un vettore

Dettagli

COGNOME E NOME (IN STAMPATELLO) MATRICOLA

COGNOME E NOME (IN STAMPATELLO) MATRICOLA Politecnico di Milano Facoltà di Ingegneria dell Informazione Informatica 3 Proff. Campi, Ghezzi, Matera e Morzenti Seconda prova in itinere 4 Luglio 2006 COGNOME E NOME (IN STAMPATELLO) MATRICOLA Risolvere

Dettagli

FILE E INDICI Architettura DBMS

FILE E INDICI Architettura DBMS FILE E INDICI Architettura DBMS Giorgio Giacinto 2010 Database 2 Dati su dispositivi di memorizzazione esterni! Dischi! si può leggere qualunque pagina a costo medio fisso! Nastri! si possono leggere le

Dettagli

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati Algoritmi e Strutture Dati Modelli di calcolo e metodologie di analisi Domenico Fabio Savo 1 Notazione asintotica f(n) = tempo di esecuzione / occupazione di memoria di un algoritmo su input di dimensione

Dettagli

Introduzione alla programmazione Algoritmi e diagrammi di flusso. Sviluppo del software

Introduzione alla programmazione Algoritmi e diagrammi di flusso. Sviluppo del software Introduzione alla programmazione Algoritmi e diagrammi di flusso F. Corno, A. Lioy, M. Rebaudengo Sviluppo del software problema idea (soluzione) algoritmo (soluzione formale) programma (traduzione dell

Dettagli

Esercizi di Algoritmi e Strutture Dati

Esercizi di Algoritmi e Strutture Dati Esercizi di Algoritmi e Strutture Dati Moreno Marzolla marzolla@cs.unibo.it Ultimo aggiornamento: 3 novembre 2010 1 Trova la somma/1 Scrivere un algoritmo che dati in input un array A[1... n] di n interi

Dettagli

Laboratorio di Calcolo B 67

Laboratorio di Calcolo B 67 Generazione di numeri casuali Abbiamo già accennato all idea che le tecniche statistiche possano essere utili per risolvere problemi di simulazione di processi fisici e di calcoli numerici. Dobbiamo però

Dettagli

Fondamenti di Informatica. Algoritmi di Ricerca e di Ordinamento

Fondamenti di Informatica. Algoritmi di Ricerca e di Ordinamento Fondamenti di Informatica Algoritmi di Ricerca e di Ordinamento 1 Ricerca in una sequenza di elementi Data una sequenza di elementi, occorre verificare se un elemento fa parte della sequenza oppure l elemento

Dettagli

Altro di ADT. Dominio. Funzioni. Dominio. Funzioni. Costanti - Collezioni

Altro di ADT. Dominio. Funzioni. Dominio. Funzioni. Costanti - Collezioni di astratto Abstract Data Type Paolo Bison Fondamenti di Informatica 1 A.A. 2004/05 Università di Padova definizione di un indipendente dalla sua rappresentazione interna e dalla effettiva implementazione

Dettagli

TABELLE AD INDIRIZZAMENTO DIRETTO

TABELLE AD INDIRIZZAMENTO DIRETTO Tabelle Servono per implementare dizioniari in modo efficiente. dizionari insiemi di coppie (chiave, valore). Esempio: in un compilatore di un linguaggio di programmazione viene creata una tabella dei

Dettagli

Nell informatica esistono alcuni problemi particolarmente rilevanti, poiché essi:

Nell informatica esistono alcuni problemi particolarmente rilevanti, poiché essi: Pag 24 3) Il problema della ricerca Nell informatica esistono alcuni problemi particolarmente rilevanti, poiché essi: si incontrano in una grande varietà di situazioni reali; appaiono come sottoproblemi

Dettagli

argomenti Hashing Funzioni hash per file Tabelle hash Funzioni hash e metodi per generarle Inserimento e risoluzione delle collisioni Eliminazione

argomenti Hashing Funzioni hash per file Tabelle hash Funzioni hash e metodi per generarle Inserimento e risoluzione delle collisioni Eliminazione Hashing argomenti Hashing Tabelle hash Funzioni hash e metodi per generarle Inserimento e risoluzione delle collisioni Eliminazione Funzioni hash per file Hashing estendibile Hashing lineare Hashing 2

Dettagli

Liste concatenate. Violetta Lonati

Liste concatenate. Violetta Lonati Liste concatenate Violetta Lonati Università degli studi di Milano Dipartimento di Informatica Laboratorio di algoritmi e strutture dati Corso di laurea in Informatica 2 novembre 2017 Violetta Lonati Liste

Dettagli

Algoritmi di Ricerca. Esempi di programmi Java

Algoritmi di Ricerca. Esempi di programmi Java Fondamenti di Informatica Algoritmi di Ricerca Esempi di programmi Java Fondamenti di Informatica - D. Talia - UNICAL 1 Ricerca in una sequenza di elementi Data una sequenza di elementi, occorre verificare

Dettagli

Organizzazioni hash. Corso di Basi di Dati e Sistemi Informativi

Organizzazioni hash. Corso di Basi di Dati e Sistemi Informativi Organizzazioni hash Corso di Basi di Dati e Sistemi Informativi 1 Hashing index-based data organizations: relazione tra chiave e indirizzo esplicita tramite puntatori. hash-based organizations: relazione

Dettagli

Algoritmi di ordinamento

Algoritmi di ordinamento Algoritmi di ordinamento! Selection Sort! Quick Sort! Lower bound alla complessità degli algoritmi di ordinamento Ordinamento 1 Selection Sort SelectionSort(dati[]) { for (i=0; idati.length-1; i++) { min

Dettagli

Heap e Code di Priorità

Heap e Code di Priorità Heap e Code di Priorità heap heap = catasta condizione di heap 1. albero binario perfettamente bilanciato 2. ogni nodo contiene una chiave maggiore o eguale di quelle presenti negli eventuali figli non

Dettagli

Così come per le liste, un dizionario è costituito da un insieme di coppie di elementi (k,e) chiaveelemento.

Così come per le liste, un dizionario è costituito da un insieme di coppie di elementi (k,e) chiaveelemento. DIZIONARI Così come per le liste, un dizionario è costituito da un insieme di coppie di elementi (k,e) chiaveelemento. In questo testo si utilizzerà spesso il termine elemento, a volte per indicare la

Dettagli

Algoritmi di Ricerca. Esempi di programmi Java

Algoritmi di Ricerca. Esempi di programmi Java Fondamenti di Informatica Algoritmi di Ricerca Esempi di programmi Java Fondamenti di Informatica - D. Talia - UNICAL 1 Ricerca in una sequenza di elementi Data una sequenza di elementi, occorre verificare

Dettagli

Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti

Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti Alberto Montresor 19 Agosto, 2014 Alcuni degli esercizi che seguono sono associati alle rispettive soluzioni. Se il vostro lettore PDF lo consente,

Dettagli

Risoluzione delle collisioni con indirizzamento aperto

Risoluzione delle collisioni con indirizzamento aperto Risoluzione delle collisioni con indirizzamento aperto Con la tecnica di indirizzamento aperto tutti gli elementi stanno nella tavola. La funzione hash non individua una singola cella ma un ordine in cui

Dettagli

Hash Tables. Ilaria Castelli A.A. 2009/2010. Università degli Studi di Siena Dipartimento di Ingegneria dell Informazione

Hash Tables. Ilaria Castelli A.A. 2009/2010. Università degli Studi di Siena Dipartimento di Ingegneria dell Informazione Hash Tables Ilaria Castelli castelli@dii.unisi.it Università degli Studi di Siena Dipartimento di Ingegneria dell Informazione A.A. 2009/2010 I. Castelli Hash Tables, A.A. 2009/2010 1/42 Hash Tables Indirizzamento

Dettagli

INDICI PER FILE. Accesso secondario. Strutture ausiliarie di accesso

INDICI PER FILE. Accesso secondario. Strutture ausiliarie di accesso INDICI PER FILE Strutture ausiliarie di accesso 2 Accesso secondario Diamo per scontato che esista già un file con una certa organizzazione primaria con dati non ordinati, ordinati o organizzati secondo

Dettagli

Algoritmi di Ordinamento

Algoritmi di Ordinamento Algoritmi di Ordinamento 1 Algoritmi di ordinamento Selection Sort Quick Sort Lower bound alla complessità degli algoritmi di ordinamento Statistiche di ordine 2 Selection Sort SelectionSort(dati[]) {

Dettagli

Implementazione ADT: Alberi

Implementazione ADT: Alberi Implementazione ADT: Alberi Livelli di astrazione 2001/2002 1 Esercitazione 5 (E6): alberi (tree) albero struttura dati fondamentale, soprattutto per le operazioni di ricerca tipi di albero con radice

Dettagli

In questa lezione Strutture dati elementari: Pila Coda Loro uso nella costruzione di algoritmi.

In questa lezione Strutture dati elementari: Pila Coda Loro uso nella costruzione di algoritmi. In questa lezione Strutture dati elementari: Pila Coda Loro uso nella costruzione di algoritmi. 1 strutture dati (astratte) Una struttura dati astratti consiste di uno o più insiemi con delle operazioni

Dettagli

Informatica 3. LEZIONE 16: Heap - Codifica di Huffmann. Modulo 1: Heap e code di priorità Modulo 2: Esempio applicativo: codifica di Huffmann

Informatica 3. LEZIONE 16: Heap - Codifica di Huffmann. Modulo 1: Heap e code di priorità Modulo 2: Esempio applicativo: codifica di Huffmann Informatica 3 LEZIONE 16: Heap - Codifica di Huffmann Modulo 1: Heap e code di priorità Modulo 2: Esempio applicativo: codifica di Huffmann Informatica 3 Lezione 16 - Modulo 1 Heap e code di priorità Introduzione

Dettagli

Algoritmi e strutture dati

Algoritmi e strutture dati Algoritmi e strutture dati Roberto Cordone A. A. 2015-16 Capitolo 4 Implementazioni delle partizioni Nota: queste dispense sono un rapido riassunto delle lezioni svolte nel dicembre 2015 e gennaio 2016.

Dettagli

Programmazione I - corso B a.a prof. Viviana Bono

Programmazione I - corso B a.a prof. Viviana Bono Università di Torino Facoltà di Scienze MFN Corso di Studi in Informatica Programmazione I - corso B a.a. 2009-10 prof. Viviana Bono Blocco 15 Algoritmi su array: selection sort, insertion sort, fusione

Dettagli

n n 1 n = > Il calcolo del fattoriale La funzione fattoriale, molto usata nel calcolo combinatorio, è così definita

n n 1 n = > Il calcolo del fattoriale La funzione fattoriale, molto usata nel calcolo combinatorio, è così definita Il calcolo del fattoriale La funzione fattoriale, molto usata nel calcolo combinatorio, è così definita n! = 1 n( n 1)! se se n n = > 0 0 dove n è un numero intero non negativo Il calcolo del fattoriale

Dettagli

Informatica Generale Andrea Corradini Gli algoritmi e la risoluzione di problemi

Informatica Generale Andrea Corradini Gli algoritmi e la risoluzione di problemi Informatica Generale Andrea Corradini 13 - Gli algoritmi e la risoluzione di problemi Sommario Passi per la risoluzione di problemi Problemi di ricerca e ordinamento Algoritmi iterativi: la ricerca lineare

Dettagli

Laboratorio di Algoritmi e Strutture Dati

Laboratorio di Algoritmi e Strutture Dati Laboratorio di Algoritmi e Strutture Dati Docente: Camillo Fiorentini 8 gennaio 8 Il problema è simile all esercizio 5.6 del libro di testo di algoritmi (Introduzione agli algoritmi e strutture dati, T.

Dettagli

Esercizi su array di array

Esercizi su array di array Corso di Laurea Ingegneria Informatica Fondamenti di Informatica Dispensa E10 C. Limongelli Gennaio 2010 1 Contenuti Fusione di due array ordinati, con eliminazione di duplicati Verifica array bidimensionale

Dettagli

A. Ferrari. algoritmi notevoli. Python. Alberto Ferrari Informatica

A. Ferrari. algoritmi notevoli. Python. Alberto Ferrari Informatica algoritmi notevoli Python algoritmi o ricerca (verificare la presenza di un valore in una sequenza) o o o ricerca sequenziale (sequenza non ordinata) ricerca sequenziale (sequenza ordinata) ricerca binaria

Dettagli

Heap e code di priorità

Heap e code di priorità Heap e code di priorità Violetta Lonati Università degli studi di Milano Dipartimento di Scienze dell Informazione Laboratorio di algoritmi e strutture dati Corso di laurea in Informatica AA 2009/2010

Dettagli

Scritto di Algoritmi e s.d. (1o anno) 5 Luglio 2005

Scritto di Algoritmi e s.d. (1o anno) 5 Luglio 2005 Scritto di Algoritmi e s.d. (1o anno) Luglio 200 Esercizio 1 (punti in prima approssimazione) Consideriamo il seguente codice C: #include #include typedef struct cella * List; struct

Dettagli

Vettori, liste e sequenze

Vettori, liste e sequenze Vettori, liste e sequenze Il concetto di Sequenza Il concetto di sequenza come insieme ordinato di oggetti è fondamentale. La sequenza è una entità lineare composta di elementi in cui ogni elemento viene

Dettagli

ORDINAMENTO PER SELEZIONE ORDINAMENTO PER SELEZIONE ORDINAMENTO VELOCE CONFRONTI

ORDINAMENTO PER SELEZIONE ORDINAMENTO PER SELEZIONE ORDINAMENTO VELOCE CONFRONTI ORDINAMENTO PER SELEZIONE Per l analisi delle prestazioni di tale algoritmo di ordinamento, si considerano i due cicli for annidati: poiché i confronti avvengono nel ciclo interno si ha che n 2 i= 0 (

Dettagli

Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti

Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti Esercizi Capitolo 10 - Code con priorità e insiemi disgiunti Alberto Montresor 27 marzo 2012 Alcuni degli esercizi che seguono sono associati alle rispettive soluzioni. Se il vostro lettore PDF lo consente,

Dettagli

Dato un insieme S di n elementi totalmente ordinato, l'algoritmo di ordinamento detto HeapSort ha le seguenti caratteristiche:

Dato un insieme S di n elementi totalmente ordinato, l'algoritmo di ordinamento detto HeapSort ha le seguenti caratteristiche: Heapsort Dato un insieme S di n elementi totalmente ordinato, l'algoritmo di ordinamento detto HeapSort ha le seguenti caratteristiche: T(n) = O(n log(n)) Alg. Ordinamento ottimale Ordina in loco (niente

Dettagli

Esercizio 1. E vero che in un AVL il minimo si trova in una foglia o nel penultimo livello? FB = -1. livello 0 FB = -1. livello 1 FB = -1.

Esercizio 1. E vero che in un AVL il minimo si trova in una foglia o nel penultimo livello? FB = -1. livello 0 FB = -1. livello 1 FB = -1. Esercizio 1 E vero che in un AVL il minimo si trova in una foglia o nel penultimo livello? FB = -1 livello 0 FB = -1 FB = -1 livello 1 FB = -1 livello 2 livello 3 L altezza è 3, il minimo si trova nel

Dettagli

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati Heap Maria Rita Di Berardini, Emanuela Merelli 1 1 Dipartimento di Matematica e Informatica Università di Camerino A.A. 2006/07 Heap Heap binari: definizione Un heap binario è una struttura dati composta

Dettagli

Problemi e algoritmi. Il che cosa ed il come. Il che cosa ed il come. Il che cosa e il come

Problemi e algoritmi. Il che cosa ed il come. Il che cosa ed il come. Il che cosa e il come Problemi e algoritmi Il che cosa e il come Problema: descrive che cosa si deve calcolare Specifica (di un algoritmo): descrive che cosa calcola un algoritmo Algoritmo: descrive come effettuare un calcolo

Dettagli

Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano. Usa la tecnica del divide et impera:

Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano. Usa la tecnica del divide et impera: MergeSort Usa la tecnica del divide et impera: 1 Divide: dividi l array a metà 2 Risolvi i due sottoproblemi ricorsivamente 3 Impera: fondi le due sottosequenze ordinate 1 Esempio di esecuzione 7 2 4 5

Dettagli

Laboratorio di Calcolo B 68

Laboratorio di Calcolo B 68 Generazione di numeri casuali Abbiamo già accennato all idea che le tecniche statistiche possano essere utili per risolvere problemi di simulazione di processi fisici e di calcoli numerici. Dobbiamo però

Dettagli

Calcolare il massimo di una lista

Calcolare il massimo di una lista Calcolare il massimo di una lista Lunedì abbiamo definito un algoritmo per calcolare il valore massimo fra gli elementi di una lista predefinita di interi. In particolare, abbiamo: deciso di rappresentare

Dettagli

Indici multilivello dinamici (B-alberi e B + -alberi) Alberi di ricerca - 1. Un esempio. Alberi di ricerca - 3. Alberi di ricerca - 2

Indici multilivello dinamici (B-alberi e B + -alberi) Alberi di ricerca - 1. Un esempio. Alberi di ricerca - 3. Alberi di ricerca - 2 INDICI MULTILIVELLO DINAMICI Indici multilivello dinamici (B-alberi e B + -alberi) Gli indici multilivello dinamici (B-alberi e B + -alberi) sono casi speciali di strutture ad albero. Un albero è formato

Dettagli

Casi di prova. Il problema dell ordinamento. Casi di prova. Casi di prova. Casi di prova

Casi di prova. Il problema dell ordinamento. Casi di prova. Casi di prova. Casi di prova Casi di prova Casi di prova Quando si vuole testare un algoritmo si devono costruire vari casi di prova. Invece di eseguire il programma più volte si può costruire un file di dati contenente tutti i casi

Dettagli

Problemi di ricerca in insiemi ordinati

Problemi di ricerca in insiemi ordinati Problemi di ricerca in insiemi ordinati Abbiamo visto che, per trovare un elemento in un insieme ordinato, con l algoritmo della ricerca binaria restringiamo l intervallo della ricerca alla metà in ogni

Dettagli

Array e Oggetti. Corso di Laurea Ingegneria Informatica Fondamenti di Informatica 1. Dispensa 12. A. Miola Dicembre 2006

Array e Oggetti. Corso di Laurea Ingegneria Informatica Fondamenti di Informatica 1. Dispensa 12. A. Miola Dicembre 2006 Corso di Laurea Ingegneria Informatica Fondamenti di Informatica 1 Dispensa 12 Array e Oggetti A. Miola Dicembre 2006 http://www.dia.uniroma3.it/~java/fondinf1/ Array e Oggetti 1 Contenuti Array paralleli

Dettagli

Informatica 3. Informatica 3. LEZIONE 23: Indicizzazione. Lezione 23 - Modulo 1. Indicizzazione. Introduzione. Indicizzazione:

Informatica 3. Informatica 3. LEZIONE 23: Indicizzazione. Lezione 23 - Modulo 1. Indicizzazione. Introduzione. Indicizzazione: Informatica 3 Informatica 3 LEZIONE 23: Indicizzazione Lezione 23 - Modulo 1 Modulo 1: Indicizzazione lineare, ISAM e ad albero Modulo 2: 2-3 trees, B-trees e B + -trees Indicizzazione lineare, ISAM e

Dettagli

In questa lezione. Il Mergesort: primo esempio di applicazione della tecnica divide et impera analisi tempo di esecuzione del Mergesort

In questa lezione. Il Mergesort: primo esempio di applicazione della tecnica divide et impera analisi tempo di esecuzione del Mergesort In questa lezione Il Mergesort: primo esempio di applicazione della tecnica divide et impera analisi tempo di esecuzione del Mergesort [CLRS] par. 2.3. Prof. E. Fachini - Intr. Alg.!1 Progettazione di

Dettagli

Problemi e algoritmi. Il che cosa e il come. F. Damiani - Alg. & Lab. 04/05 (da U. de' Liguoro - Alg. & Spe. 03/04)

Problemi e algoritmi. Il che cosa e il come. F. Damiani - Alg. & Lab. 04/05 (da U. de' Liguoro - Alg. & Spe. 03/04) Problemi e algoritmi Il che cosa e il come Il che cosa ed il come Problema: descrive che cosa si deve calcolare Specifica (di un algoritmo): descrive che cosa calcola un algoritmo Algoritmo: descrive come

Dettagli

Ordinamenti. Grafo : definizione. Un grafo G = (V,E)è composto da: V: insieme di vertici E V V: insieme di archi (edge) che connettono i vertici

Ordinamenti. Grafo : definizione. Un grafo G = (V,E)è composto da: V: insieme di vertici E V V: insieme di archi (edge) che connettono i vertici Ordinamenti 1 Vittorio Maniezzo Università di Bologna Grafo : definizione Un grafo G = (V,E)è composto da: V: insieme di vertici E V V: insieme di archi (edge) che connettono i vertici Un arco a= {u,v}

Dettagli

Algoritmi e Strutture di Dati I 1. Algoritmi e Strutture di Dati I Massimo Franceschet francesc

Algoritmi e Strutture di Dati I 1. Algoritmi e Strutture di Dati I Massimo Franceschet  francesc Algoritmi e Strutture di Dati I 1 Algoritmi e Strutture di Dati I Massimo Franceschet http://www.sci.unich.it/ francesc Algoritmi e Strutture di Dati I 2 Tabelle hash Una tabella hash (in inglese hash

Dettagli

Testo di riferimento. Problema delle 12 monete. Algoritmi. Complessità degli algoritmi (cenni) Dispense del Corso di Algoritmi e Strutture Dati

Testo di riferimento. Problema delle 12 monete. Algoritmi. Complessità degli algoritmi (cenni) Dispense del Corso di Algoritmi e Strutture Dati Testo di riferimento Complessità degli algoritmi (cenni) CORDA Informatica A. Ferrari Dispense del Corso di Algoritmi e Strutture Dati Marco Bernardo - Edoardo Bontà Università degli Studi di Urbino Carlo

Dettagli