Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Algoritmica, Appunti di Algoritmi E Strutture Di Dati

Algoritmica

Tipologia: Appunti

2015/2016
In offerta
30 Punti
Discount

Offerta a tempo limitato


Caricato il 21/09/2016

ZanteMario
ZanteMario 🇮🇹

4.4

(46)

23 documenti

1 / 28

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
RICERCA SEQUENZIALE
Ricerca un elemento k in un insieme di n elementi memorizzati in un‟array A.
Si scandisce l‟array confrontandone ogni elemento con quello ricercato.
DA UTILIZZARE QUANDO L‟ARRAY NON E‟ ORDINATO.
precondizione: a è un array di n elementi, numeri interi senza segno.
1. Ricerca Sequenziale (a, k, n):
2. trovato = false;
3. for (i = 0; i < n, i++) {
4. if (a[i] == k) {
5. trovato = true;
6. }
7. }
8. print trovato;
Il programma scandisce tutto l‟array, anche se l‟elemento è stato trovato non termina perché non gli
viene data l‟istruzione. In questo modo, il programma fa comunque n confronti.
Modifichiamo il programma in modo che faccia solo i confronti necessari usiamo un ciclo
while.
1. Ricerca SequenzialeOpt (a, k, n):
2. i =0;
3. trovato = false;
4. while (i < n) && (!trovato) {
5. if (a[i] == k) {
6. trovato = true;
7. }
8. else {
9. i++;
10. }
11. }
12. print trovato;
Quanti confronti fa questa procedura al caso peggiore?
La ricerca sequenziale ha una complessità lineare: al caso pessimo il numero di confronti è n.
ESERCIZI Costruire l‟algoritmo per la ricerca sequenziale di k nell‟ipotesi che ce ne siamo più copie e
vogliamo stampare il valore dell‟indice per ognuna delle copie. Se non trova niente, stampare -1.
1. Ricerca Sequenziale (a, k, n):
2. trovato = false;
3. for (i = 0; i < n, i++) {
4. if (a[i] == k) {
5. print i;
6. trovato = true;
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
Discount

In offerta

Anteprima parziale del testo

Scarica Algoritmica e più Appunti in PDF di Algoritmi E Strutture Di Dati solo su Docsity!

RICERCA SEQUENZIALE

 Ricerca un elemento k in un insieme di n elementi memorizzati in un‟array A.  Si scandisce l‟array confrontandone ogni elemento con quello ricercato.  DA UTILIZZARE QUANDO L‟ARRAY NON E‟ ORDINATO.

precondizione: a è un array di n elementi, numeri interi senza segno.

  1. Ricerca Sequenziale (a, k, n):
  2. trovato = false;
  3. for (i = 0; i < n, i++) {
  4. if (a[i] == k) {
  5. trovato = true;
  6. }
  7. }
  8. print trovato;

Il programma scandisce tutto l‟array, anche se l‟elemento è stato trovato non termina perché non gli

viene data l‟istruzione. In questo modo, il programma fa comunque n confronti.

Modifichiamo il programma in modo che faccia solo i confronti necessariusiamo un ciclo

while****.

  1. Ricerca SequenzialeOpt (a, k, n):
  2. i =0;
  3. trovato = false;
  4. while (i < n) && (!trovato) {
  5. if (a[i] == k) {
  6. trovato = true;
  7. }
  8. else {
  9. i++;
  10. }
  11. }
  12. print trovato;

Quanti confronti fa questa procedura al caso peggiore?

La ricerca sequenziale ha una complessità lineare : al caso pessimo il numero di confronti è n.

ESERCIZI – Costruire l‟algoritmo per la ricerca sequenziale di k nell‟ipotesi che ce ne siamo più copie e vogliamo stampare il valore dell‟indice per ognuna delle copie. Se non trova niente, stampare -1.

  1. Ricerca Sequenziale (a, k, n):
  2. trovato = false;
  3. for (i = 0; i < n, i++) {
  4. if (a[i] == k) {
  5. print i;
  6. trovato = true;
  1. if (!trovato) {
  2. print -1;
  3. }
  4. }

Qual è il minimo lavoro che posso fare per determinare se k appartiene all’insieme?

(Procediamo come per il problema delle 12 monete).

Quante possibilità ci sono? n + 1  n + la possibilità che k non appartenga all‟insieme.

Assumiamo che l‟array contenga elementi tutti distinti tra loro. Confrontiamo gli elementi a coppie.

Dopo 1 confronto posso discriminare tra 2 soluzioni (=possibilità).

Dopo 2 confronti posso discriminare tra 4 (2^2 ) soluzioni.

Dopo 3 confronti posso discriminare tra 8 (2^3 ) soluzioni.

In questo caso 2i^ ≥ n +1  dopo i confronti, discrimino tra 2i^ situazioni.

𝟐𝒊^ ≥ 𝒏 + 𝟏 → = 𝐥𝐨𝐠𝟐 𝟐𝒊^ ≥ 𝐥𝐨𝐠𝟐(𝒏 + 𝟏) → 𝒊 ≥ 𝐥𝐨𝐠𝟐( 𝒏 + 𝟏)

Il numero minimo di confronti per trovare k in un‟array di n elementi è log 2 (n + 1).

Al caso ottimo O(log 2 n).

x : y

z : w z : w

2 ,^

4 ,^

8 ,^ …^ ,^

2 𝑖^ = 1

O meglio:

i ·1 ( i volte 1)

𝑛,

21 ,^

22 ,^

23 ,^ …^ ,^

2 𝑖^ = 1

N.B. Dopo i + 1 volte si ottiene 1 (n = 2i^  n/2i^ = 1).

𝒏 𝟐𝒊^

= 𝟏 → 𝒏 = 𝟐𝒊^ → 𝐥𝐨𝐠𝟐 𝒏 = 𝐥𝐨𝐠𝟐 𝟐𝒊^ → 𝒊 = 𝐥𝐨𝐠𝟐 𝒏

Nel caso pessimo, i passi da fare sono log 2 n

Per n elementi, i confronti necessari al caso pessimo sono:

n 2 10 100 1000 10000 1000000 log 2 n 1 ~4 ~6 ~9 ~13 ~

Con circa 24 confronti si può cercare un elemento tra 1.000.000 di dati.

Formula  con n elementi, servono i confronti  confronti = log 2 elementi

Attenzione! Ricerca Binaria si può utilizzare solo se la sequenza di elementi è ordinata. In caso contrario, si usa Ricerca Sequenziale.

ESERCIZI - Modificare l‟algoritmo affinché restituisca l‟indice della posizione dell‟elemento trovato.

Ricerca Binaria(a, k): sinistra = 0; destra = n - 1; while (sinistra <= destra) { centro = (sinistra + destra)/2; if (a[centro] == k) { return centro; } else if (a[centro] > k) { destra = centro - 1; } else { sinistra = centro + 1; } } return -1;

RICORSIONE

 Un algoritmo è ricorsivo quando è definito in termini di se stesso.  Perché è utile la ricorsione? o Permette di scrivere programmi compatti. o Può migliorare la velocità di esecuzione.

Esempio  il fattoriale si calcola in maniera ricorsiva:

𝑁! = 𝑁 𝑁 − 1 𝑁 − 2 𝑁 − 3 … 1 (*)

FATTORIALE

  1. Fattoriale (N):
  2. k = 1;
  3. for (i = 1; i < N; i++) {
  4. k = k * i; // si cambia l‟ordine delle moltiplicazioni rispetto a (*)
  5. }
  6. return k;

Abbiamo scritto un algoritmo iterativo; come visto, il fattoriale si calcola ricorsivamente, quindi scriviamo un algoritmo ricorsivo.

  1. RFatt (N):
  2. if (N ==1) {
  3. return 1; //condizione di terminazione
  4. }
  5. else {
  6. return N* (RFatt(N -1)); //si chiama ricorsivamente RFatt e il risultato si moltiplica per N.
  7. }

Simulazione :

RFatt(5)

La procedura chiamata RFatt(4) restituisce 24, che viene moltiplicato per N = 5.

RFatt(5) restituisce 24*5 = 120.

RFatt(4)

La procedura chiamata RFatt(3) restituisce 6, che viene moltiplicato per N = 4.

RFatt(4) restituisce 6*4 = 24 alla procedura chiamante e si chiude.

RFatt(3)

La procedura chiamata RFatt(2) restituisce 2, che viene moltiplicato per N = 3.

RFatt(3) restituisce 2*3 = 6 alla procedura chiamante e si chiude.

RFatt(2)

La procedura chiamata RFatt(1) restituisce 1, che viene moltiplicato per

N = 2.

RFatt(2) restituisce 2 alla procedura chiamante e si chiude.

RFatt(1)

if (N ==1) return 1  questo viene restituito alla procedura

chiamante.

Questa procedura si chiude.

Qual è l’algoritmo migliore tra Ricerca Binaria e RRICBIN?

Da un lato Ricerca Binaria è migliore perché le operazioni ricorsive hanno un costo maggiore (serve più

memoria, i parametri e le variabili devono essere memorizzati).

Dall‟altro, l’algoritmo ricorsivo in genere è migliore quando la natura stessa del problema è

ricorsiva.

In questo caso specifico, si dimostra che il numero di confronti da effettuare è identico.

Dimostrazione - Complessità di RRICBIN ricavata con le equazioni di ricorrenza

Poniamo n = 2i

In questo caso:

Nella seconda equazione C(n/2) si spiega ricordando che RRICBIN viene chiamata su uno solo dei due

insiemi. La costante 1 rappresenta il confronto con l’elemento centrale.

Possiamo riscrivere di volta in volta C(n/2x); esempi:

N.B. tanti 1 quanto l’esponente di 2 (tre 1, 2^3 …)

Finiremo con n/2i^ = 1.

Avremo alla fine allora:

Dove la somma degli 1 è uguale a i. Ma:

2 𝑖^

Quindi avremo:

Diventa:

Rimane da trovare i:

n = 2i^  log 2 n = log 22 i^ 

Complessità di RRICBIN:

i + 1 è il numero di confronti fatto da RRICBIN

i = log 2 n

FIBONACCI

Per il problema della ricerca binaria, il numero di confronti da effettuare rimaneva identico, sia utilizzando la ricorsione, sia no. In altri casi, l’algoritmo ricorsivo è peggiore a causa della complessità. Un esempio è l‟algoritmo che ricava i numeri di Fibonacci.

1. RFIB(n): 2. if (n == 0) { 3. return 0; 4. } 5. if (n == 1) { 6. return 1; 7. } 8. else { 9. return (RFIB(n - 1) + RFIB(n - 2)); 10. }

Questo è un uso disastroso della ricorsività  si calcola sempre la stessa cosa!

Il tempo di esecuzione è dato da queste equazioni:

𝑇 0 = 𝑇 1 = 𝑐 (𝑐𝑜𝑠𝑡𝑎𝑛𝑡𝑒) 𝑇 𝑛 = 𝑇 𝑛 − 1 + 𝑇 𝑛 − 2 + 𝑐

Se si risolve la seconda equazione, si ottiene che

𝒏 𝟐 (^) < 𝑇 𝒏 < 𝟐𝒏

Quando la dimensione del problema (n) è esponente del tempo di computazione, la crescita del problema è esponenzialeanche se la base è molto piccola!

Supponiamo di dover fare 2n^ operazioni e di svolgere una operazione al secondo. n 5 10 15 20 25 35 40 tempo 32 secondi 17 minuti 9 ore 12 giorni 1 anno 1089 anni 34865 anni

Per migliorare quest‟algoritmo possiamo operare in due modi:  memorizzare i numeri di Fibonacci in un‟array;  memorizzare come variabili solo gli ultimi due elementi calcolati.

  1. Fib(n):
  2. if (n == 0) print 0;
  3. if (n == 1) print 1;
  4. else {
  5. a = 0;
  6. b = 1;
  7. for (i = 2; i < n; i++) { [inizio da 2 perché 0 e 1 sono già calcolati]
  8. c = a + b;
  9. a = b;
  10. b = c;
  11. }
  12. print c;
  13. }

Quante operazioni compie questa procedura? O(n)  il ciclo si compie n -1 volte, le operazioni da svolgere sono 3 quindi circa 3n  siamo passati da 2 n^ ad un numero proporzionale ad n, l‟algoritmo è molto migliore.

Simulazione con 4 dischi

TdH(4, 1, 2, 3);

PRINT 1  3

TdH(3, 1, 3, 2);

PRINT 1  2

TdH(2, 1, 2, 3);

PRINT 1  3

TdH(1, 1, 3, 2) PRINT 1  2

TdH(1, 2, 1, 3) PRINT 2  3

TdH(2, 3, 1, 2)

PRINT 3  2

TdH(1, 3, 2, 1) PRINT 3  1

TdH(1, 1, 3, 2) PRINT 1  2

TdH(3, 2, 1, 3)

PRINT 2  3

TdH(2, 2, 3, 1)

PRINT 2  1

TdH(1, 2, 1, 3) PRINT 2  3

TdH(1, 3, 2, 1) PRINT 3  1

TdH(2, 1, 2, 3)

PRINT 1  3

TdH(1, 1, 3, 2) PRINT 1  2

TdH(1, 2, 1, 3) PRINT 2  3

  1. TdH(n, primo, secondo, terzo):
  2. if (n ==1) {
  3. print “primo”  “terzo”;
  4. }
  5. else {
  6. TdH(n – 1, primo, terzo, secondo);
  7. print “primo”  “terzo”;
  8. TdH(n - 1, secondo, primo, terzo);
  9. }

Per n dischi, le mosse minime per

risolvere il problema sono 2n^ – 1

O(2n)

Esempio: 4 dischi

Mosse necessarie: 2^4 – 1 = 15

Come visto, Ricerca Binaria [O(logn)] è molto più efficiente di Ricerca Sequenziale [O(n)]. Se l‟array è ordinato infatti, esso viene suddiviso confrontando k con l‟elemento centrale.

Questa è una tecnica usata molto spesso, chiamata divide et impera :

DIVIDE ET IMPERA – tecnica in 3 passi

  1. Dividi il problema da risolvere in sottoproblemi;
  2. Risolvi i sottoproblemi: o ricorsivamente; o direttamente se sono piccoli (caso base  condizione di terminazione che fa sì che l‟algoritmo abbia un numero di operazioni finito);
  3. Trova la soluzione generale dalla combinazione delle soluzioni dei sottoproblemi.

[Con Ricerca Binaria sono sufficienti i primi due passi]

Problemi correlati: ricerca del massimo in un insieme.

MASSIMO – Ricerca del massimo in un insieme non ordinato

Algoritmo banale

 Si scandisce l‟array confrontandone ogni elemento con il primo; se questo risulta maggiore, diventa il massimo.  Confronti necessari: n – 1 per dimensione dell‟input: n.

  1. Massimo(a, n):
  2. Max = a[0];
  3. for (i = 1; i < n; i++) {
  4. if (a[i] > Max) {
  5. Max = a[i];
  6. }
  7. }
  8. return Max.

Algoritmo divide et impera

 Si divide l‟array in due sottoarray di n/2 elementi ciascuno.  Si trova il massimo M1 del primo sottoarray ricorsivamente (o direttamente se n = 1) e si trova il massimo M2 del secondo sottoarray.  Si combinano le soluzioni, trovando il massimo tra M1 e M2. Confronti necessari: n – 1.

  1. RMassimo (a, sx, dx):
  2. if (sx == dx) { // condizione di terminazione
  3. Max = a[sx];
  4. }
  5. cx = (sx + dx)/2;
  6. M1 = RMassimo(a, sx, cx);
  7. M2 = RMassimo(a, cx + 1, dx);
  8. if (M1 < M2) {
  9. Max = M2;
  10. }
  11. else {
  12. Max = M1;
  13. }
  14. return Max;

N.B.: nella prima riga potremmo scrivere RMassimo(a, 0, n -1) ma questa istruzione sarebbe valida solo

all‟inizio; utilizziamo sx e dx perché è necessario che siano variabili.

La procedura continua a dividere l‟array finché arriva ad ottenere sottoinsiemi di singoli elementi.

Se abbiamo l‟array

2 8 3 10 6 5 7 9 0 1 2 3 4 5 6 7

La soluzione di un problema di 8 elementi si rimanda alla soluzione di due sottoproblemi di 4 elementi.

La procedura continua a dividere l‟array finchè non ottiene elementi singoli di cui può restituire il risultato.

Nella fase TOP-DOWN si divide il problema in sottoproblemi più piccoli.

Nella fase BOTTOM-UP si fanno i confronti.

N.B. Avevamo 8 elementi , abbiamo fatto 7 (8 - 1) confronti.

Supponiamo di voler ottenere, senza simulazione, il numero di confronti. Un metodo è risolvere l‟ equazione di ricorrenza (scritta in termini di se stessa).

𝐶 𝑛 = 0 𝑠𝑒 𝑛 = 1 𝐶 𝑛 = 2𝐶

2 + 1^ 𝑠𝑒^ 𝑛^ > 1

C(n) = numero di confronti fatti da RMassimo su n elementi.

N.B. si aggiunge 1  rappresenta il confronto finale.

Nell‟equazione per n > 1 si ha 2*C(n/2) perché RMassimo è chiamato ricorsivamente su n/2 due volte.

Simulazione per n = 2i

Utilizziamo la seconda equazione:

𝐶 𝑛 = 2𝐶

Possiamo riscrivere C(n/2) come:

𝐶

2 = 2𝐶^

E quindi

𝐶 𝑛 = 1 + 2 1 + 2𝐶

Ma possiamo scrivere anche C(n/4) come:

𝐶

4 = 2𝐶^

E ottenere così:

𝐶 𝑛 = 1 + 2 + 4 1 + 2𝐶 𝑛 8

che risolto è: 𝐶 𝑛 = 2^0 + 2^1 + 2^2 + 2^3 𝐶 2 𝑛 3

Continuiamo a dividere per 2, fino ad arrivare ad insiemi di un solo elemento (1 = n/2i, dove n = 2i):

𝐶 𝑛 = 2^0 + 2^1 + 2^2 + 2^3 + … + 2𝑖^ 𝐶 𝑛 2 𝑖

Ma 𝐶 2 𝑛𝑖 = 𝐶(1) perché n = 2i^ 

Dobbiamo svolgere le operazioni:

C(n) = 2i^ C(1) + 2i^ –^1 + 2i -2^ + … + 2^0.

Poiché per n = 1 C(n) = 0, si ha 2i·0 = 0 e quindi:

C(n) = 2i^ –^1 + 2i -2^ + … + 2^0.

Per una regola matematica 2^0 + 2^1 + 2^2 + … + 2i^ ^1 = 2i^ – 1

Sappiamo che n = 2i Quindi possiamo scrivere semplicemente:

C(n) = n – 1

Questo metodo consente di determinare la complessità di un algoritmo.

CONCLUSIONI - Abbiamo visto che sia Massimo che RMassimo necessitano di n – 1 confronti:

n – 1 rappresenta il limite inferiore

Quanti confronti fa questa procedura? (n -1) + (n - 2) + (n - 3) + …. + 1

E‟ il numero di confronti necessario all‟ordinare l‟array, considerando che l‟array si riduce ogni volta di un elemento. Ma quest‟espressione è uguale anche a:

𝟐 =^ 𝒇𝒐𝒓𝒎𝒖𝒍𝒂^ 𝒅𝒊^ 𝑮𝒂𝒖𝒔𝒔

Al crescere di n, il termine quadratico diventa molto più preponderante  diciamo quindi che questa procedura è:

O(n^2 ) = numero di confronti da fare

N.B. questo numero rimane invariato, sia al caso pessimo che al caso ottimo si devono fare O(n^2 ) confronti!

QUICK SORT

 usa il Divide et impera;  algoritmo migliore per l‟ordinamento;  usa un passo casuale  meccanismo  si prende l‟ultimo elemento del‟array (posizione fissa ma valore casuale); si confrontano tutti gli elementi dell‟array con questo elemento; quelli minori si metteranno a sinistra, quelli maggiori a destra. Si posiziona poi il perno (l‟ultimo elemento dell‟array) nella giusta posizione. Quindi, si suddivide l‟array in 2 parti: minori del perno e maggiori del perno. N.B. fatto ciò, gli elementi non sono ancora ordinati! Si usa ricorsivamente la stessa procedura:

  1. dividi secondo il valore del perno;
  2. risolvi direttamente o ricorsivamente;

La suddivisione dell‟array in due parti non è perfetta  i sottoarray possono avere dimensioni diverse, e l‟algoritmo funziona meglio tanto più le partizioni sono bilanciate.

precondizione: per semplicità si suppone che l‟array abbia elementi tutti distinti tra loro

  1. QuickSort(a, sx, dx):
  2. if (sx < dx) {
  3. // scegli come perno l‟elemento a[dx]
  4. perno = Distribuzione(a, sx, dx);
  5. QuickSort(a, sx, perno -1);
  6. QuickSort(a, perno+1, dx);
  7. }

Note:

riga 1  all‟inizio l‟algoritmo lavorerà su (a, 0, n - 1);

riga 2  sx == dx  condizione di terminazione.

riga 4  sceglie l‟ultimo elemento dell‟array; ci permette di sapere in che posizione è il perno rispetto agli altri elementi, dopo aver spostato i minori del perno a sinistra e i maggiori del perno a destra.

righe 5-6  per effettuare le chiamate ricorsive dobbiamo prima separare gli elementi < a[dx] dagli elementi > a[dx].

SIMULAZIONE

5 30 21 11 9 39 29 14 32 4 35 6 28 24

 Si sceglie come perno a[dx] = 24;  Si utilizzano due variabili: i e j;  Lavoriamo con i : finché gli elementi sono < 24 non vanno spostati, gli altri sì.  Iniziamo da i = 0  a[0] = 5  5 < 24? SI‟, non viene spostato.  i = 1  a[1] = 30  30 < 24? NO, smetto di lavorare con i e continuo con j.  j lavora in modo opposto, sposta gli elementi minori e lascia stare gli elementi maggiori.  Iniziamo dall‟ultimo elemento prima del perno: 28 > 24? SI‟  non viene spostato.  Continuiamo con 6 > 24? NO  scambiamo il 6 con il 30.

5 6 21 11 9 39 29 14 32 4 35 30 28 24

 Lavoriamo con i : 21, 11, 9 < 24? SI‟  non vengono spostati

Quanti confronti in totale verranno fatti dall’indice i e dall’indice j?

Il costo di distribuzione è O(n), costo simile al numero di confronti necessari  gli indici proseguono senza mai tornare indietro.

Se le partizioni sono bilanciate, QuickSort funziona bene  al caso pessimo preso il perno potremmo avere un sottoarray degenere e un sottoarray con tutti gli altri elementi.  caso in cui l‟array era già stato ordinato.

𝑻 𝒏 = 𝟎 𝒑𝒆𝒓 𝒏 = 𝟏 𝑻 𝒏 = 𝑶 𝒏 + 𝑻 𝒅 + 𝑻 𝒏 − 𝒅 − 𝟏 𝒑𝒆𝒓 𝒏 > 1

Se n  0 confronti;

Se n > 1  T(n) = O(n) + T(d) + T(n – d -1). [gli ultimi due rappresentano il tempo sull‟insieme di dx e quello sull‟insieme di sx, il primo il costo di distribuzione].

Al caso pessimo T(n) = O(n) + T(n -1) (infatti avremo un insieme vuoto e uno di n – 1 elementi  manca d che rappresenta una partizione degli elementi!)

Quanto vale?

T(n) = O(n) + T(n -1)

T(n -1) = (n – 1) + T(n – 2)

T(n) = n + (n – 1) + T(n -2)

T(n -2) = (n – 2) + T(n -3)

T(n) = n + (n – 1) + (n – 2) + T(n – 3)

…potrei continuare fino a trovare T(1).

T(n) = n + (n – 1) + (n – 2) + …..+ T(1).

Distribuzione si realizza in un tempo proporzionale al numero di elementi.

Per la legge di Gauss:

e allora ha un costo stimabile in O(n^2 ).

EQUAZIONE DI RICORRENZA DI QUICKSORT - L’equazione di ricorrenza di questa procedura è:

equazione molto difficile da risolvere perché q è variabile

Al caso pessimo l’equazione di ricorrenza è:

𝑇 𝑛 = 𝑇 𝑛 − 1 + 𝑂 𝑛 = 𝑂(𝑛^2 )

Esempio:

Alla destra del perno (20) non riusciamo a mettere nulla  caso pessimo.

CASO OTTIMO DEL QUICKSORT

Al caso ottimo l’insieme viene diviso in due sottoinsiemi più o meno della stessa dimensione ( partizioni

bilanciate ), e il perno si trova circa a metà.

L’equazione di ricorrenza sarà:

T(n/2) è il tempo di ogni chiamata ricorsiva.

Calcoliamone il valore per n = 2i:

Riscriviamo T(n/2) come:

E ancora:

Avremo quindi:

O meglio:

In genere bastano tre passi per capire che equazione si sviluppa.