1 / 92

Lezione 12

Lezione 12. B-Alberi Algoritmi su grafi. Sommario. B-Alberi definizione ricerca inserimento Rappresentazione dei grafi Visita in ampiezza Visita in profondità Ordinamento topologico. B-Alberi. I B-Alberi sono una generalizzazione degli alberi binari di ricerca

ringo
Download Presentation

Lezione 12

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Lezione 12 B-Alberi Algoritmi su grafi

  2. Sommario • B-Alberi • definizione • ricerca • inserimento • Rappresentazione dei grafi • Visita in ampiezza • Visita in profondità • Ordinamento topologico

  3. B-Alberi • I B-Alberi sono una generalizzazione degli alberi binari di ricerca • la principale differenza è che i B-Alberi • ogni nodo dell’albero può contenere n>2 chiavi • il grado di un nodo è alto (50-2000) • i B-Alberi sono utilizzati per garantire l’efficienza delle operazioni su insiemi dinamici (ricerca, inserzione e cancellazione) di dati memorizzati su supporti secondari (dischi)

  4. Visualizzazione M D H Q T X B C F G J K L N P Y Z R S V W

  5. Memorie Secondarie • La memoria primaria (RAM) si basa su una tecnologia costosa ma che permette di eseguire le operazioni di scrittura e lettura in modo veloce • la memoria secondaria (dischi) è più lenta (vi sono componenti meccaniche da muovere), ma più economica • questo permette di rendere disponibile una quantità di memoria secondaria di uno o due ordini di grandezza maggiore della memoria primaria

  6. Memorie Secondarie • Le informazioni in un disco sono organizzate in blocchi • il blocco minimo accessibile in lettura e scrittura è detto pagina • una pagina corrisponde a circa 2 MB

  7. Accesso alla memoria secondaria • Per trattare quantità estremamente grandi di dati si devono pertanto sviluppare algoritmi che lavorino con dati memorizzati in memoria secondaria • si devono pertanto minimizzare gli accessi alla memoria oltre che garantire efficienza computazionale di CPU • Le operazioni di accesso ai dati negli algoritmi vengono modificate in: x=puntatore a un dato Disk-Read(x) …operazioni di elaborazione di x Disk-Write(x) …operazioni che accedono a x in sola lettura

  8. Accesso alla memoria secondaria • Le operazioni di lettura su disco si intendono fatte nel caso in cui il dato puntato da x non sia già disponibile nella memoria primaria • le operazioni di scrittura vengono invece eseguite solo se il dato puntato da x è stato in qualche modo modificato

  9. B-Alberi • In ogni istante è possibile mantenere in memoria primaria solo un numero limitato di pagine • le operazioni eseguite su i B-Alberi garantiscono di poter essere eseguite conservando solo un numero costante di pagine in memoria principale (tante più pagine tanto più efficienti saranno le varie operazioni) • in genere un nodo di un B-Albero e tanto grande quanto una pagina di memoria secondaria • Nota: nel presentare gli algoritmi si trascurerà la gestione di basso livello della memoria

  10. B-Alberi • Per semplicità si suppone di memorizzare in un nodo solo la chiave dei dati • un eventuale puntatore associato alla chiave servirà per indirizzare la pagina del disco su cui trovare i dati satellite

  11. Definizione dei B-Alberi • un B-Albero è un albero radicato T che soddisfa le seguenti proprietà: • ogni nodo x è caratterizzato dai seguenti attributi: • n[x] numero delle chiavi memorizzate in x • le n[x] chiavi sono memorizzate in ordine decrescente • leaf[x] è true se il nodo è una foglia, false altrimenti • un nodo interno x contiene n[x]+1 puntatori c1[x], c2[x],…, cn[x]+1[x] ai suoi figli (o NIL se x è una foglia) • i campi keyi[x] definiscono gli intervalli delle chiavi memorizzate in ciascun sottoalbero: se ki è una qualunque chiave memorizzata nel sottoalbero di radice ci[x] allora k1 key1[x]  k2  key2[x]  …  keyn[x][x]  kn[x]+1 • tutte le foglie sono alla stessa profondità, che coincide con l’altezza dell’albero

  12. Definizione dei B-Alberi • il numero delle chiavi per ogni nodo è limitato sia inferiormente che superiormente in funzione di un intero t chiamato grado minimo del B-Albero • t  2 • ogni nodo (eccetto la radice) contiene almeno t-1 chiavi • ogni nodo interno (eccetto la radice) ha almeno t figli • ogni nodo può contenere al massimo 2t-1 chiavi • ogni nodo interno può avere al massimo 2t figli • un nodo è detto pieno se contiene esattamente 2t-1 chiavi

  13. Altezza di un B-Albero • Un B-Albero con n chiavi e grado minimo t ha una altezza h  logt (n+1)/2 • Infatti: il caso peggiore è che un B-Albero abbia una radice con un’unica chiave e che tutti i nodi contengano il numero minimo di chiavi, cioè t-1 • a profondità 1 ci saranno pertanto 2 nodi, a profondità 2, 2t nodi, a profondità 3, 2t2 nodi. • Ogni nodo contiene t-1 chiavi • pertanto il numero totale di chiavi n deve essere: • n  1 + (t-1)i=1..h 2ti-1= 1+2(t-1)(th-1)/(t-1)=2th-1 • ovvero h  logt (n+1)/2

  14. Operazioni sui B-Alberi • La radice del B-Albero è sempre in memoria principale • non devono pertanto essere effettuate operazioni di Disk-Read per leggere la radice • tuttavia se si modifica la radice deve essere eseguita una operazione di Disk-Write • si suppone che per tutti i nodi passati come parametro alle varie procedure si sia correttamente compiuta l’operazione di Disk-Read • tutte le procedure che vedremo sono a “singola passata” cioè algoritmi che visitano l’albero a partire dalla radice e non risalgono mai indietro

  15. Ricerca • E’ un operazione simile alla ricerca sugli alberi binari di ricerca • la differenza è che non ci sono solo due vie possibili ad ogni nodo, ma n[x]+1 • la procedura B-Tree-Search • prende in ingresso il puntatore alla radice dell’albero e la chiave da cercare • restituisce la coppia ordinata (y,i) che consiste di un puntatore a nodo y e un indice i tale che keyi[y]=k

  16. Pseudocodice per la Ricerca B-Tree-Search(x,k) 1 i  1 2 while i  n[x] e k > keyi[x] 3 do i  i+1 4 if i  n[x] e k = keyi[x] 5 then return (x,i) 6 if leaf[x] 7 then return NIL 8 else DISK-READ(ci[x]) 9 return B-Tree-Search(ci[x],k)

  17. Spiegazione pseudocodice • Nelle linee 1-3 si esegue una ricerca lineare per trovare il più piccolo indice i tale che k  keyi[x] • in 4-5 si controlla se la chiave è stata trovata • altrimenti 6-9 o siamo in una foglia e la ricerca termina senza successo • o procediamo ricorsivamente su un opportuno sottoalbero del nodo in esame che contiene chiavi comprese fra un valore sicuramente più piccolo di k e uno più grande

  18. Visualizzazione Ricerca della chiave R M D H Q T X B C F G J K L N P Y Z R S V W

  19. Analisi • La ricerca procede dalla radice lungo un cammino verso una foglia • il numero di accessi è pertanto O(h)=O(logtn) • poiché il numero di chiavi in un nodo è n[x]<2t la ricerca lineare 2-3 impiega per esaminare un qualsiasi nodo un tempo O(t) • il tempo complessivo sarà pertanto O(t logtn)

  20. Costruzione di un B-Albero • Per costruire un B-Albero si utilizza una procedura B-Tree-Create per creare un nodo radice vuoto • poi si utilizza la procedura B-Tree-Insert per inserire ogni nodo • entrambe queste procedure fanno uso di una procedura ausiliaria Allocate-Node() che ha il compito di creare un nuovo nodo e di assegnargli una opportuna pagina del disco in tempo O(1)

  21. Pseudocodice per la costruzione della radice di un B-Albero B-Tree-Create 1 x  Allocate-Node() 2 leaf[x]  true 3 n[x]  0 4 Disk-Write(x) 5 root[T]  x

  22. Divisione di un nodo in un B-Albero • L’operazione di inserzione di un nodo è complicata dal fatto che se la nuova chiave deve essere memorizzata in un nodo pieno allora bisogna procedere a dividere questo nodo in due • un nodo pieno y con 2t-1 chiavi viene diviso in due nodi di t-1 chiavi all’altezza della chiave mediana keyt[y] • la chiave mediana viene spostata nel nodo padre • se y è la radice si aumenta l’altezza dell’albero: è infatti questo il meccanismo di crescita dei B-Alberi

  23. Visualizzazione keyi-1[x] keyi[x] keyi[x] keyi+1[x] x … N W …. … N S W …. y=ci[x] P Q R S T U V P Q R T U V y=ci[x] z=ci+1[x]

  24. Idea intuitiva • La procedura ha come parametri un nodo interno x non pieno, un indice i e un nodo y pieno. • y è il figlio i-esimo di x. • In origine y ha 2t-1 chiavi, dopo la divisione rimane con i t-1 chiavi minori • un nuovo nodo z acquisisce i t-1 chiavi maggiori e diventa un figlio di x dopo y • la chiave mediana di y viene rimossa da y e posta in x e diventa la chiave che separa y da z

  25. Divisione di un nodo B-Tree-Split-Child(x,i,y) 1 z  Allocate-Node() 2 leaf[z]  leaf[y] 3 n[z]  t-1 4 for j  1 to t-1 5 do keyj[z]  keyj+t[y] 6 if not leaf[y] 7 then for j  1 to t 8 do cj[z]  cj+t[y] 9 n[y]  t-1 10 for j  n[x]+1 downto i+1 11 do cj+1[x]  cj[x] 12 cj+1[x]  z 13 for j  n[x] downto i 14 do keyj+1[x]  keyj[x] 15 keyi[x]  keyt[y] 16 n[x]  n[x]+1 17 Disk-Write(y); Disk-Write(z); Disk-Write(x)

  26. Spiegazione dello pseudocodice • Le linee 1-8 creano un nuovo nodo z e gli assegnano le t-1 chiavi più grandi di y, assieme ai figli corrispondenti • in 10-14 si inserisce z come nuovo figlio di x • in 15 si inserisce la chiave mediana di y come separatore • in 16 si modifica il contatore delle chiavi n[x] • in 17 si riporta su disco le modifiche effettuate

  27. Analisi • Il tempo di esecuzione è dominato dai cicli alle linee 4 o 7 o 10 o 13 che impiegano tutti un tempo limitato superiormente da O(t)

  28. Inserimento di una nuova chiave • L’inserimento di una nuova chiave può avvenire in due casi: • quando il nodo radice è pieno • quando il nodo radice non è pieno • La procedura B-Tree-Insert inserisce una nuova chiave k in un B-Albero e gestisce il caso in cui si debba inserire la chiave in una radice piena • in questo caso si aumenta di 1 l’altezza dell’albero inserendo una nuova radice • ci si riporta così al caso di inserimento in un albero con radice non piena che viene trattato dalla procedura B-Tree-Insert-Nonfull

  29. Visualizzazione root[T] H s root[T] A D F H L N P A D F L N P r r

  30. Pseudocodice per l’inserimento di una nuova chiave B-Tree-Insert(T,k) 1 r  root[T] 2 if n[r] = 2t-1 3 then s  Allocate-Node() 4 root[T]  s 5 leaf[s]  false 6 n[s]  0 7 c1[s]  0 8 B-Tree-Split-Child(s,1,r) 9 B-Tree-Insert-NonFull(s,k) 10 else B-Tree-Insert-NonFull(r,k)

  31. Inserimento in nodo non pieno • La procedura è organizzata in modo tale da essere chiamata sempre solo su nodi non pieni • la procedura distingue il caso in cui si debba inserire la nuova chiave in un nodo foglia o si debba scendere ricorsivamente in un nodo interno • per un nodo foglia si deve gestire la collocazione della chiave nella giusta posizione e aggiornare il numero di chiavi • per un nodo interno si deve verificare che questo non sia pieno per poter applicare ricorsivamente la B-Tree-Insert-Nonfull • nel caso in cui sia un nodo pieno si richiama la procedura B-Tree-Split-Child

  32. Pseudocodice per l’inserimento B-Tree-Insert-Nonfull(x,k) 1 i  n[x] 2 if leaf[x] 3 then while i1 e k<keyi[x] 4 do keyi+1[x]  keyi[x] 5 i  i-1 6 keyi+1[x]  k 7 n[x]  n[x]+1 8 Disk-Write(x) 9 else while i1 e k<keyi[x] 10 do i  i -1 11 i  i+1 12 Disk-Read(ci[x]) 13 if n[ci[x]]=2t-1 14 then B-Tree-Split-Child(x,i,ci[x]) 15 if k > keyi[x] 16 then i  i+1 17 B-Tree-Insert-Nonfull(ci[x],k)

  33. Spiegazione pseudocodice • In 3-8 ci si occupa del caso di inserimento della chiave nel nodo foglia: si determina la posizione della chiave facendole contemporaneamente posto • in 9-17 si considera il caso in cui si debba scendere ricorsivamente attraverso nodi interni • in 9-11 si determina quale figlio esaminare • in 13 se il figlio è pieno si divide e in 15-16 si determina per quale dei due nuovi sotto figli si debba proseguire • in 17 si procede ricorsivamente su un nodo figlio sicuramente non pieno fino a raggiungere una foglia

  34. Visualizzazione inserzione della chiave B in B-Albero con t=3 G M P X A C D E J K N O R S T U V Y Z G M P X A B C D E J K N O R S T U V Y Z

  35. Visualizzazioneinserzione della chiave Q G M P X A B C D E J K N O R S T U V Y Z G M P T X A B C D E J K N O Q R S Y Z U V

  36. Visualizzazioneinserzione della chiave L G M P T X A B C D E J K N O Q R S Y Z U V P G M T X A B C D E L J K N O Q R S Y Z U V

  37. Visualizzazioneinserzione della chiave F P G M T X A B C D E L J K N O Q R S Y Z U V P C G M T X A B L J K N O Q R S Y Z D E F U V

  38. Analisi • Per un B-Albero di altezza h la procedura B-Tree-Insert effettua O(h) accessi al disco • infatti: • questa richiama la procedura B-Tree-Insert-Nonfull ricorsivamente su un numero di nodi al più numeroso come il massimo cammino fino ad una foglia (h) • inoltre la procedura B-Tree-Insert-Nonfull esegue un numero O(1) di operazioni di lettura-scrittura

  39. Analisi • per il tempo di computazione di CPU si ha che B-Tree-Insert-Nonfull ha un ciclo O(t) (linea 3 o 9) • inoltre richiama una volta la procedura B-Tree-Split-Child che costa O(t) • dato che B-Tree-Insert-Nonfull viene chiamata ricorsivamente al più O(h) volte si ha complessivamente un costo O(th)=O(t logtn)

  40. Grafi • I grafi sono strutture dati molto diffuse in informatica • Vengono utilizzati per rappresentare reti e organizzazioni dati complesse e articolate • Per elaborare i grafi in genere è necessario visitarne in modo ordinato i vertici • Vedremo a questo proposito due modi fondamentali di visita: per ampiezza e per profondità

  41. Nota sulla notazione asintotica • Il tempo di esecuzione di un algoritmo su un grafo G=(V,E) viene dato in funzione del numero di vertici |V| e del numero di archi |E| • Utilizzando la notazione asintotica adotteremo la convenzione di rappresentare |V| con il simbolo V e |E| con E: quando diremo che il tempo di calcolo è O(E+V) vorremo significare O(|E|+|V|)

  42. Rappresentazione di un grafo • Vi sono due modi per rappresentare un grafo: • collezione di liste di adiacenza • matrice di adiacenza • si preferisce la rappresentazione tramite liste di adiacenza quando il grafo è sparso, cioè con |E| molto minore di |V|2 • si preferisce la rappresentazione tramite matrice di adiacenza quando, al contrario, il grafo è denso o quando occorre alta efficienza nel rilevare se vi è un arco fra due vertici dati

  43. Liste di adiacenza • Si rappresenta un grafo G=(V,E) con un vettore Adj di liste, una lista per ogni vertice del grafo • per ogni vertice u, Adj[u] contiene tutti i vertici v adiacenti a u, ovvero quei vertici v tali per cui esiste un arco (u,v)E • in particolare questo insieme di vertici è memorizzato come una lista • l’ordine dei vertici nella lista è arbitrario

  44. Visualizzazione:grafo non orientatocon liste di adiacenza 1 2 2 1 2 3 4 5 5 1 3 4 5 2 4 3 2 5 3 4 1 2 5 4

  45. Visualizzazione:grafo orientato con liste di adiacenza 1 2 1 2 3 4 5 6 2 4 5 6 5 3 2 4 4 5 6 6

  46. Proprietà della rappresentazione con liste di adiacenza • Se un grafo è orientato allora la somma delle lunghezze di tutte le liste di adiacenza è |E| • infatti per ogni arco (u,v) c’è un vertice v nella lista di posizione u • Se un grafo non è orientato allora la somma delle lunghezze di tutte le liste di adiacenza è 2|E| • infatti per ogni arco (u,v) c’è un vertice v nella lista di posizione u e un vertice u nella lista di posizione v • La quantità di memoria necessaria per memorizzare un grafo (orientato o non) è O(max(V,E)) = O(V+E)

  47. Grafi pesati • In alcuni problemi si vuole poter associare una informazione (chiamata peso) ad ogni arco • un grafo con archi con peso si dice grafo pesato • si dice che esiste una funzione peso che associa ad un arco un valore w : E  R • ovvero un arco (u,v) ha peso w(u,v)

  48. Grafi pesati con liste di adiacenza • Si memorizza il peso w(u,v) insieme al vertice v nella lista per il vertice u

  49. Visualizzazione:grafo orientato pesato con liste di adiacenza 0.2 1 2 1 2 3 4 5 6 2 0.2 4 0.3 5 0.4 0.1 6 0.2 5 0.6 0.4 3 0.3 2 0.1 0.6 0.2 4 0.5 4 5 0.8 6 0.5 6 0.8

  50. Svantaggi della rappresentazione con liste di adiacenza • Per sapere se un arco (u,v) è presente nel grafo si deve scandire la lista degli archi di u

More Related