7.5 Problemi
7.4-3 q 1)2 raggiunge un massimo nell’intervallo q = Dimostrate che q 2 + (n 0, 1, . . . , n 1 quando q = 0 o q = n 1.
−
− −
−
7.4-4 Dimostrate che il tempo di esecuzione atteso di R ANDOMIZED -Q UICKSORT e` Ω(n lg n). 7.4-5 Il tempo di esecuzione di quicksort pu`o essere migliorato in pratica sfruttando il fatto che insertion sort viene eseguito rapidamente se il suo input `e “quasi” ordinato. Quando quicksort viene chiamato a operare su un sottoarray con meno di k elementi, lasciatelo terminare senza ordinare il sottoarray. Dopo che la chiamata principale di quicksort e` completata, eseguite insertion sort sull’intero array per finire il processo di ordinamento. Dimostrate che questo algoritmo di ordinamento viene eseguito nel tempo atteso O(nk + n lg(n/k)). Come dovrebbe essere scelto k in teoria e in pratica? 7.4-6 Modificate la procedura PARTITION selezionando casualmente tre elementi dall’array A e partizionando rispetto alla loro mediana (il valore centrale dei tre elementi). Approssimate la probabilit`a di ottenere nel caso peggiore una ripartizione α-a-(1 α) come una funzione di α nell’intervallo 0 < α < 1 .
−
7.5
Problemi
7-1 Correttezza della partizione di Hoare La versione di PARTITION presentata in questo capitolo non e` l’algoritmo originale di partizionamento. La versione originale, che e` stata ideata da C. A. R. Hoare, e` questa: H OARE -PARTITION (A,p,r) 1 2 3 4 5 6 7 8 9 10 11
← A[ p] ← p − 1 ← r + 1
x i j
while TRUE j 1 do repeat j until A[ j] x i+1 repeat i until A[i] x if i < j then scambia A[i] else return j
← − ≤ ← ≥
↔ A[ j]
a. Descrivete l’operazione di HOARE -PARTITION sull’array A = 13, 19, 9, 5, 12, 8, 7, 4, 11, 2, 6, 21 , indicando i valori dell’array e i valori ausiliari dopo ogni iterazione del ciclo while nelle righe 4–11.
Le prossime tre domande richiedono un attento ragionamento sulla correttezza della procedura H OARE -PARTITION . Dimostrate che: b. Gli indici i e j sono tali che non sar`a possibile accedere a un elemento di A esterno al sottoarray A[ p . . r].
133
134
Capitolo 7 - Quicksort
c. Quando H OARE -PARTITION termina, restituisce un valore di j tale che p j < r .
≤
d. Ogni elemento di A[ p . . j] e` minore o uguale a qualsiasi elemento di A[ j + 1 . . r] quando H OARE -PARTITION termina. La procedura PARTITION nel Paragrafo 7.1 separa il valore del pivot (originariamente in A[r]) dalle due partizioni che forma. La procedura HOARE -PARTITION , d’altra parte, inserisce sempre il valore del pivot (originariamente in A[ p]) in una delle due partizioni A[ p . . j] e A[ j + 1 . . r]. Poich´e p j < r , questa ripartizione non e` mai banale.
≤
e. Riscrivete la procedura QUICKSORT per utilizzare H OARE -PARTITION . 7-2 Analisi alternativa di quicksort Un’analisi alternativa del tempo di esecuzione dell’algoritmo quicksort randomizzato si focalizza sul tempo di esecuzione atteso di ogni singola chiamata ricorsiva di Q UICKSORT , anzich´e sul numero di confronti effettuati. a. Dimostrate che, dato un array di dimensione n , la probabilit`a che un particolare elemento sia scelto come pivot e` 1/n. Sulla base di questa dimostrazione definite le variabili casuali indicatrici X i = I l’i-esimo elemento pi`u piccolo e` scelto come pivot . Qual e` il valore atteso E [X i ]?
{
}
b. Sia T (n) una variabile casuale che indica il tempo di esecuzione di quicksort con un array di dimensione n . Dimostrate che
n
E [T (n)] = E
q=1
X q (T (q
− 1) + T (n − q ) + Θ(n))
(7.5)
c. Dimostrate che l’equazione (7.5) pu`o essere riscritta cos`ı:
2 n−1 E [T (n)] = E [T (q )] + Θ(n) n q=2
(7.6)
d. Dimostrate che n 1
−
k lg k
k=2
≤ 12 n2 lg n − 18 n2
(7.7)
(Suggerimento: scomponete la sommatoria in due parti, una per k = 2, 3, . . . , n/2 1 e l’altra per k = n/2 , . . . , n 1.)
−
−
e. Applicando il limite dell’equazione (7.7), dimostrate che la soluzione della ricorrenza nell’equazione (7.6) e` E [T (n)] = Θ(n lg n) (suggerimento: dimostrate, per sostituzione, che E [T (n)] an lg n per valori di n sufficientemente grandi e per qualche costante positiva a ).
≤
7-3 Algoritmo Stooge-Sort I professori Howard e Fine hanno ideato il seguente “elegante” algoritmo di ordinamento:
Ruolo degli algoritmi nell’elaborazione dei dati
Che cosa sono gli algoritmi? Perch´e e` utile studiare gli algoritmi? Qual e` il ruolo degli algoritmi rispetto ad altre tecnologie utilizzate nei calcolatori? In questo capitolo risponderemo a queste domande.
1.1 1.1
Algo Algori ritm tmii
Informalmente, un algoritmo e` una procedura di calcolo ben definita che prende un certo valore, o un insieme di valori, come input e genera un valore, o un insieme di valori, come output. Un algorit algoritmo mo e` quindi una sequenza di passi computazionali che trasforma l’input in output. Possiamo anche considerare un algoritmo come uno strumento per risolvere un problema computazionale computazionale ben definito. La definizione del problema specifica in termini generali la relazione di input/output desiderata. L’algoritmo descrive una specifica procedura computazionale per ottenere tale relazione di input/output. Per esempio, supponiamo di dovere ordinare una sequenza di numeri in ordine non decrescente. Questo problema si presenta spesso nella pratica e rappresenta un terreno fertile per introdurre vari strumenti di analisi e tecniche di progettazione standard. Il problema dell’ordinamento pu` puo` essere formalmente definito nel seguente modo:
Input: una sequenza di n numeri a1 , a2 , . . . , a . n
Output: una permutazione (riordinamento) a1 , a2 , . . . , a della sequenza di a . input tale che a 1 a2
≤ ≤ · ··· · ≤
n
n
Per esempio, esempio, data la sequenza sequenza di input input 31, 41, 59, 26, 41, 58 , un algorit algoritmo mo di ordiordinamento restituisce come output la sequenza 26, 31, 41, 41, 58, 59 . Tale sequenza di input e` detta istanza del problema dell’ordinamento. In generale, l’istanza di un problema e` formata dall’input (che soddisfa tutti i vincoli imposti nella definizione del problema) richiesto per calcolare una soluzione del problema. L’ordinamento e` un’operazione fondamentale in informatica (molti programmi la usano come passo intermedio), per questo sono stati sviluppati vari algoritmi di ordinamento. La scelta dell’algoritmo pi` u appropriato a una data applicazione dipende – fra l’altro – dal numero di elementi da ordinare, dal livello di ordinamento iniziale degli elementi, da eventuali vincoli sui valori degli elementi e dal tipo di unit`a di memorizzazione da utilizzare: memoria principale, dischi o nastri. Un algoritmo si dice corretto se, per ogni istanza di input, termina con l’output corretto. Diciamo che un algoritmo corretto risolve il problema computazionale dato. Un algoritmo errato potrebbe non terminare affatto con qualche istanza di input o potrebbe terminare fornendo una soluzione diversa da quella desiderata. Contrariamente a quello che uno potrebbe aspettarsi, gli algoritmi errati a vol-
1
7.5 Problemi
S TOOGE -S ORT (A,i,j) 1 2 3 4 5 6 7 8
if A[i] > A[ j] then scambia A[i] if i + 1 j then return
≥ k ← ( j − i + 1)/3
↔ A[ j] Suddivisione. Primi due terzi. Ultimi due terzi. Ancora i primi due terzi.
− −
S TOOGE -S ORT (A,i,j k) S TOOGE -S ORT (A, i + k, j) S TOOGE -S ORT (A,i,j k)
a. Dimostrate che, se n = lunghezza [A], allora la chiamata STOOGE -S ORT (A, 1, lunghezza [A]) ordina correttamente l’array di input A[1 . . n]. b. Scrivete una ricorrenza e un limite asintotico stretto (notazione Θ) per il tempo di esecuzione nel caso peggiore di S TOOGE -S ORT . c. Confrontate il tempo di esecuzione nel caso peggiore di S TOOGE-S ORT con quello di insertion sort, merge sort, heapsort e quicksort. Questi professori sono degni del ruolo che occupano? 7-4 Profondit` a dello stack per quicksort L’algoritmo QUICKSORT descritto nel Paragrafo 7.1 contiene due chiamate ricorsive a s e´ stesso. Dopo la chiamata di PARTITION , prima viene ordinato ricorsivamente il sottoarray sinistro e poi quello destro. La seconda chiamata ricorsiva in Q UICKSORT non e` veramente necessaria; pu`o essere evitata utilizzando una struttura di controllo iterativa. Questa tecnica, detta ricorsione in coda, e` fornita automaticamente dai buoni compilatori. Considerate la seguente versione di quicksort, che simula la ricorsione in coda. Q UICKSORT (A,p,r) 1 while p < r 2 do Partiziona e ordina il sottoarray sinistro 3 q PARTITION (A,p,r) 4 Q UICKSORT (A,p,q 1) p q + 1 5
← ←
−
a. Dimostrate che QUICKSORT (A, 1, lunghezza [A]) ordina correttamente gli elementi dell’array A . I compilatori di solito eseguono le procedure ricorsive utilizzando uno stack (o pila) che contiene informazioni pertinenti a ogni chiamata ricorsiva, inclusi i valori dei parametri. Le informazioni per la chiamata piu` recente si trovano in cima allo stack, mentre le informazioni per la chiamata iniziale si trovano in fondo allo stack. Quando una procedura viene chiamata, le sue informazioni vengono inserite ( push) nello stack; quando termina, le sue informazioni vengono estratte ( pop) dallo stack. Poich´e si presuppone che i parametri dell’array siano rappresentati da puntatori, le informazioni per ogni chiamata di procedura sullo stack occupano uno spazio O(1) nello stack. La profondit` a dello stack `e la quantit`a massima di spazio utilizzato nello stack in un istante qualsiasi durante un processo.
135
1.1 Algori Algoritmi tmi
provider di servizi Internet potrebbe essere interessato a determinare dove allocare delle risorse addizionali per servire i suoi clienti in modo pi`u efficiente. Tutti questi sono esempi di problemi che possono essere risolti utilizzando la programmazione lineare, che sar`a trattata nel Capitolo 29. Sebbene alcuni dettagli di questi esempi esulino dagli scopi di questo libro, tuttavia e` opportuno descrivere le tecniche di base che si applicano a questi tipi di problemi. Spiegheremo inoltre come risolvere molti problemi concreti, inclusi i seguenti: •
•
•
Supponiamo di avere una carta stradale dove sono segnate le distanze fra ogni coppia di incroci adiacenti; il nostro obiettivo e` determinare il percorso pi` u breve da un incrocio all’altro. Il numero di percorsi possibili pu`o essere enorme, anche se escludiamo i percorsi che passano su s´ e stessi. Come scegliere il pi`u breve di tutti i percorsi? In questo caso, creiamo un modello della carta stradale (che a sua volta e` un modello delle strade reali) come un grafo (che descriveremo descriveremo nel Capitolo 10 e nell’Appendice nell’Appendice B) e cerchiamo di determinadeterminare il cammino pi`u breve da un vertice all’altro del grafo. Spiegheremo come risolvere efficientemente questo problema nel Capitolo 24.
···
Data una sequenza A1 , A2 , . . . , A di n matrici, vogliamo determinare il A . Poich´e la moltiplicazione di matrici e` associativa, loro prodotto A 1 A2 associativa, ci sono vari modi di moltiplicare. Per esempio, se n = 4, potremmo eseguire il prodotto delle matrici in uno dei seguenti modi: (A1 (A2 (A3 A4 ))), (A1 ((A2 A3 )A4 )), ((A1 A2 )(A3 A4 )), ((A1 (A2 A3 ))A4 ) o (((A1 A2 )A3 )A4 ). Se le matrici sono tutte quadrate (e quindi della stessa dimensione), il modo di moltiplicare le matrici non avr`a effetto sul tempo richiesto per eseguire il prodotto. Se, invece, queste matrici hanno dimensioni differenti (ma compatibili con la moltiplicazione delle matrici), allora il modo di moltiplicare pu` o determinare una differenza significativa. Il numero dei possibili modi di moltiplicare le matrici e` esponenziale in n , pertanto provare tutti i possibili modi potrebbe richiedere un tempo molto lungo. Vedremo nel Capitolo 15 come utilizzare una tecnica generale, generale, la programmazione programmazione dinamica, per risolvere questo questo problema in una maniera molto pi` u efficiente. n
n
b (mod n), dove a, b e n sono interi, vogliamo deData l’equazione ax terminare tutti gli interi x, modulo n, che soddisfano l’equazione. Ci possono essere zero, una o pi`u soluzioni. Potremmo semplicemente provare x = 0, 1, . . . , n 1 nell’ordine, ma il Capitolo 31 descrive un metodo pi`u efficiente. efficiente.
≡
−
•
Dati n punti nel piano, vogliamo determinare il guscio convesso di questi punti. Il guscio convesso e` il pi`u piccolo poligono convesso che contiene i punti. Intuitivamente, possiamo immaginare ogni punto come se fosse rappresentato da un chiodo che fuoriesce da una tavola. Il guscio convesso potrebbe essere rappresentato da un elastico teso che circonda tutti i chiodi. Ogni chiodo attorno al quale l’elastico fa un giro e` un vertice del guscio convesso (un esempio e` illustrato nella Figura 33.6 a pagina 805). Uno dei 2 sottoinsiemi dei punti potrebbe essere formato dai vertici del guscio convesso. Conoscere i punti che formano i vertici del guscio convesso non e` sufficiente, in quanto occorre sapere anche l’ordine in cui essi si presentano. Ci sono dunque molte possibilit` a di scelta per i vertici del guscio convesso. Il Capitolo 33 descrive due buoni metodi per trovare il guscio convesso. n
7
136
Capitolo 7 - Quicksort
b. Descrivete uno scenario in cui la profondit`a dello stack di Q UICKSORT `e Θ(n) con un array di input di n elementi. c. Modificate il codice di QUICKSORT in modo che lo stack nel caso peggiore sia Θ(lg n). Mantenete il tempo di esecuzione atteso O(n lg n). 7-5
Partizione con la mediana-di-3
Un modo per migliorare la procedura R ANDOMIZED -Q UICKSORT consiste nell’effettuare il partizionamento scegliendo opportunamente (non casualmente) il pivot nel sottoarray. Un tipico approccio e` il metodo della mediana-di-3: scegliere come pivot la mediana (elemento centrale) di un insieme di 3 elementi selezionati a caso dal sottoarray (vedere l’Esercizio 7.4-6). Per questo problema, supponete che gli elementi nell’array di input A[1 . . n] siano distinti e che n 3. Indicate con A [1 . . n] l’array di output ordinato. Applicando il metodo della mediana-di-3 per scegliere il pivot x , definite p i = Pr x = A [i] .
≥
{
}
a. Scrivete una formula esatta di p i in funzione di n e i per i = 2, 3, . . . , n (notate che p 1 = p n = 0).
−1
b. Di quanto deve essere aumentata la probabilit`a di scegliere come pivot x = A [ (n + 1)/2 ], la mediana di A[1 . . n], rispetto all’implementazione normale? Supponete che n e indicate il rapporto limite di queste probabilit`a.
→∞
c. Se definiamo “buona” una ripartizione in cui il pivot scelto e` x = A [i], dove n/3 i 2n/3, di quanto deve essere aumentata la probabilit`a di ottenere una buona ripartizione rispetto all’implementazione normale? (Suggerimento: approssimate la sommatoria con un integrale.)
≤ ≤
d. Dimostrate che nel tempo di esecuzione Ω(n lg n) di quicksort il metodo della mediana-di-3 influisce soltanto sul fattore costante. 7-6 Ordinamento fuzzy degli intervalli Considerate un problema di ordinamento in cui i numeri non sono noti con esattezza. Piuttosto, per ogni numero, conosciamo un intervallo nella retta reale cui appartiene il numero. In altre parole, conosciamo n intervalli chiusi nella forma [ai , bi ], dove ai bi . L’obiettivo consiste nell’effettuare un ordinamento fuzzy di questi intervalli, cio`e produrre una permutazione i1 , i2 , . . . , in degli intervalli in modo che esista un valore c j [aij , bij ] che soddisfa c 1 c2 cn .
≤
∈
≤ ≤ · ·· ≤
a. Progettate un algoritmo che effettua un ordinamento fuzzy di n intervalli. L’algoritmo deve avere la struttura generale di quicksort che ordina rapidamente gli estremi sinistri degli intervalli (i termini ai ), ma deve sfruttare la sovrapposizione degli intervalli per migliorare il tempo di esecuzione (all’aumentare della sovrapposizione degli intervalli, il problema dell’ordinamento fuzzy degli intervalli diventa sempre piu` facile). b. Dimostrate che, in generale, il tempo di esecuzione atteso del vostro algoritmo e` Θ(n lg n); questo tempo, per`o, diventa Θ(n) quando tutti gli intervalli si sovrappongono (cio`e quando esiste un valore x tale che x [ai , bi ] per ogni i). Il vostro algoritmo non deve essere esplicitamente verificato per questo caso speciale; piuttosto, le sue prestazioni dovrebbero migliorare naturalmente all’aumentare dell’entit`a della sovrapposizione degli intervalli.
∈