Você está na página 1de 35

Quicksort

O problema da ordenao consiste em rearranjar um


vetor v[0..n1] em ordem crescente, ou seja, permutar
os elementos do vetor de modo que
tenhamos v[0] v[1] . . . v[n1]. O algoritmo
Quicksort, inventado por C.A.R. Hoare em 1962, resolve
o problema.
Em algumas raras instncias, o Quicksort pode ser to
lento quanto os algoritmos elementares; mas em geral
muito mais rpido. Mais precisamente, o algoritmo
consome tempo proporcional a n log n em mdia e
proporcional a n no pior caso.
2

Usaremos duas abreviaturas ao discutir o algoritmo. A


expresso v[i..k]?x ser usada como abreviatura
de v[j]?x para todoj no conjunto de ndices i..k.
Analogamente, a expresso v[i..k]?v[p..r] ser
interpretada como v[j]?v[q] para todo j no
conjunto i..k e todo q no conjunto p..r.

O problema da separao

O ncleo do algoritmo Quicksort o seguinte problema


da separao (= partition subproblem), que
formularemos de maneira propositalmente vaga:
rearranjar um vetor v[p..r] de modo que todos os
elementos pequenos fiquem na parte esquerda do
vetor e todos os elementos grandes fiquem na parte
direita.
O ponto de partida para a soluo deste problema a
escolha de um piv (ou fulcro), digamos c. Os
elementos do vetor que forem maiores que c sero
considerados grandes e os demais sero considerados
pequenos. importante escolher c de tal modo que as
duas partes do vetor rearranjado sejam estritamente
menores que o vetor todo. A dificuldade est em
resolver o problema da separao de maneira rpida
sem usar muito espao de trabalho.
O problema da separao admite muitas formulaes
concretas. Eis uma primeira: rearranjar v[p..r] de
modo que tenhamos
v[p..j]?v[j+1..r]

para algum j em p..r1. (Nessa formulao, o piv no


explcito.) Eis outra formulao:
rearranjar v[p..r] de modo que
v[p..j1]?v[j]<v[j+1..r]

para algum j em p..r. (Aqui, v[j] o piv.) Neste


captulo, usaremos a segunda formulao. Segue um
exemplo do resultado da separao:
777

222

111

777

999

444

555

666

666

222

111

777

555

444

555

777

555

888

999

888

Exerccios 1

1. POSITIVOS E NEGATIVOS. Escreva uma funo que rearranje um


vetor v[p..r] de nmeros inteiros de modo que tenhamos v[p..j1]
?0 e v[j..r]>0 para algum j em p..r+1. Faz sentido exigir
que j esteja em p..r? Procure escrever uma funo rpida que no
use vetor auxiliar. Repita o exerccio depois de trocar v[j..r]>
0 por v[j..r]?0. Faz sentido exigir que v[j] seja 0?
2. LISTA. Suponha dada uma lista encadeada que armazena nmeros
inteiros. Escreva uma funo que transforme a lista em duas: a
primeira contendo os nmeros pares e a segunda contendo os
mpares.
3. Critique a seguinte tentativa de resolver o problema da separao:
4. intsep(intv[],intp,intr){
5. inti,t;
6. for(i=p+1;i<=r;i++)
7. if(v[p]>v[i]){
8. t=v[p];v[p]=v[i];v[i]=t;}
9. returnp;}

10.Critique a seguinte soluo do problema da separao:


11. intsep(intv[],intp,intr){
12. intw[1000],i=p,j=r,c=v[p],k;
13. for(k=p+1;k<=r;++k)
14. if(v[k]<=c)w[i++]=v[k];
15. elsew[j]=v[k];
16. //agorai==j
17. w[i]=c;
18. for(k=p;k<=r;++k)v[k]=w[k];
19. returni;}

20.Um programador inexperiente afirma que a seguinte funo resolve a


primeira formulao do problema da separao, ou seja, rearranja
qualquer vetor v[p..r] (com p<r) de tal forma que v[p..i]?
v[i+1..r] para algum i em p..r1.
21. intsep(intv[],intp,intr){
22. intq,i,j,t;
23. i=p;q=(p+r)/2;j=r;
24. do{

25. while(v[i]<v[q])++i;
26. while(v[j]>v[q])j;
27. if(i<=j){
28. t=v[i],v[i]=v[j],v[j]=t;
29. ++i,j;}
30. }while(i<=j);
31. returni;}

Mostre um exemplo em que essa funo no d o resultado


prometido. E se trocarmos returni por returni1? possvel fazer
algumas poucas correes de modo que a funo d o resultado
esperado?
32. Digamos que um vetor v[p..r] est separado se existe j em p..r tal
que v[p..j1]?v[j]<v[j+1..r] . Escreva um algoritmo que
decida se v[p..r] est separado. Em caso afirmativo, o seu algoritmo
deve devolver o ndice j.

O algoritmo da separao
Eis como D. Gries implementa o algoritmo da
separao. A funo rearranja o vetor v[p..r] e
devolve um elemento j do conjunto p..rtal que v[p..j
1]?v[j]<v[j+1..r]:
intsepara(intv[],intp,intr)
{
intc=v[p],i=p+1,j=r,t;//1
while(1){//2
while(i<=r&&v[i]<=c)++i;//3
while(c<v[j])j;//4
if(i>=j)break;//5
t=v[i],v[i]=v[j],v[j]=t;//6
++i;j;//7
}//8
v[p]=v[j],v[j]=c;//9

returnj;//10
}

Gostaramos que j ficasse a meio caminho entre p e r,


mas devemos estar preparados para aceitar os casos
extremos em que j vale pou r.
p

?c ?c ?c ?c

>c >c >c >c >c

Algumas perguntas que chamam a ateno para


detalhes importantes: Que acontece se executarmos a
funo com p igual a r? Que acontece se eliminarmos i
<=r na linha 3? Por que no h um j>=p na linha 4?
Que acontece se escrevermos i>j na linha 5? Que
acontece se trocarmos j por i nas linhas 9 e 10?

Anlise do algoritmo da separao. Eu prefiro


implementar a funo separa de maneira um pouco
diferente a fim de facilitar a anlise de sua correo:
//Recebevetorv[p..r]comp<=r.Rearranja

//oselementosdovetoredevolvejemp..r

//talquev[p..j1]<=v[j]<v[j+1..r].

staticint

separa(intv[],intp,intr)

intc=v[p],i=p+1,j=r,t;

while(/*A*/i<=j){

if(v[i]<=c)++i;

elseif(c<v[j])j;

else{

t=v[i],v[i]=v[j],v[j]=t;

++i;j;

//agorai==j+1

v[p]=v[j],v[j]=c;

returnj;

(A palavra-chave static est a apenas para indicar


que separa uma funo auxiliar e no deve ser
invocada diretamente pelo usurio final do Quicksort.)
No incio de cada iterao, ou seja, a cada passagem
pelo ponto A, temos a seguinte configurao:
p

?c ?c ?c

>c >c

Na penltima passagem pelo ponto A, teremos uma das


configuraes a seguir:
p

?c ?c ?c >c ?c >c >c >c >c

i=j

?c ?c ?c ?c

>c >c >c >c

Portanto, na ltima passagem pelo ponto A, valem as


seguintes relaes:

i==j+1,
p?j?r,
v[p+1..j]?c e v[j+1..r]>c.
Assim, v[p..j1]?v[j]<v[j+1..r] na fim da
execuo da funo, como prometido.
(Observe que a varivel i serve de sentinela para j e
vice-versa. Isso torna desnecessrias as comparaes
de i com r toda vez que i incrementado e torna
desnecessrias as comparaes de j com p toda vez
que j decrementado. Esse detalhe contribui para
tornar a funo particularmente rpida.)

Desempenho do algoritmo da separao.


Quanto tempo a funo consome? O consumo de
tempo proporcional ao nmero de comparaes entre
elementos do vetor. No difcil perceber que esse
nmero proporcional ao tamanho rp+1 do
vetor.

Exerccios 2
1. Mostre o resultado da operao separa(v,0,15), sendo v[0..15] o
vetor
2. 33225533442299665511887733886666

3. Qual o resultado da funo separa quando os elementos


de v[p..r] so todos iguais? E quando v[p..r] crescente?
E quando v[p..r] decrescente? E quando cada elemento
de v[p..r] tem um de dois valores possveis?
4. VERSO RECURSIVA. Escreva uma verso recursiva da funo separa.
5. Reescreva o cdigo de separa de modo que v[r] seja escolhido para
servir de piv.
6. A funo separa produz um rearranjo estvel do vetor, ou seja,
preserva a ordem relativa de elementos de mesmo valor?

7. SEPARAO DE LOMUTO. Analise e discuta a seguinte verso da


funo separa. (O livro de Cormen, Leiserson, Rivest e Stein e atribui
essa verso a N. Lomuto.)
8. intsepara_L(intv[],intp,intr){
9. intc=v[r],j=p,k,t;
10. for(k=p;k<r;++k)
11. if(v[k]<=c){
12. t=v[j],v[j]=v[k],v[k]=t;
13. ++j;}
14. t=v[j],v[j]=v[r],v[r]=t;
15. returnj;}

16.Critique a seguinte variante da funo separa:


17. intsepara(intv[],intp,intr){
18. intc=v[p],i=p+1,j=r,t;
19. while(i<=j){
20. if(v[i]<=c)++i;
21. else{
22. t=v[i],v[i]=v[j],v[j]=t;
23. j;}}
24. v[p]=v[j],v[j]=c;
25. returnj;}

26.Critique a seguinte variante da funo separa:


27. intsepara(intv[],intp,intr){
28. intc=v[p],i=p+1,j=r,t;
29. while(i<=j)
30. if(v[i]<=c){
31. v[i1]=v[i];
32. ++i;}
33. else{
34. t=v[i],v[i]=v[j],v[j]=t;
35. j;}

36. v[j]=c;
37. returnj;}

38.Critique a seguinte soluo do probleam da separao (extrada


do livro de E. Roberts).
39. intsepara_R(intv[],intp,intr){
40. intc=v[p],i=p+1,j=r,t;
41. while(1){
42. while(i<j&&c<v[j])j;
43. while(i<j&&v[i]<=c)++i;
44. if(i==j)break;
45. t=v[i],v[i]=v[j],v[j]=t;}
46. if(v[j]>c)returnp;
47. v[p]=v[j],v[j]=c;
48. returnj;}

49.DESAFIO. Escreva uma soluo do problema da separao que devolva


um ndice j tal que (rp)/10?jp?9(rp)/10,
50.Um vetor binrio se cada um de seus elementos vale 0 ou 1.
Escreva uma funo que rearranje um vetor binrio v[0..n1] em
ordem crescente e consuma tempo proporcional ao tamanho do
vetor.

Quicksort bsico
Agora que resolvemos o problema da separao,
podemos cuidar do Quicksort propriamente dito. O
algoritmo usa a estratgia dadiviso e conquista e tem
a aparncia de um Mergesort ao contrrio:
//Estafunorearranjaqualquervetor

//v[p..r]emordemcrescente.

void

quicksort(intv[],intp,intr)

intj;//1

if(p<r){//2

j=separa(v,p,r);//3

quicksort(v,p,j1);//4

quicksort(v,j+1,r);//5

Se p?r (essa a base recurso) no preciso fazer


nada. Se p<r, o cdigo reduz a instncia v[p..r] do
problema ao par de instncias v[p..j1] e v[j+1..r].
Como p?j?r, essas duas instncias so estritamente
menores que a instncia original. Assim, por hiptese
de induo, v[p..j1] estar em ordem crescente no
fim da linha 4 e v[j+1..r] estar em ordem crescente
no fim da linha 5. Como v[p..j1]?v[j]<
v[j+1..r] graas funo separa, o vetor todo estar
em ordem crescente no fim da linha 5, como promete a
documentao da funo.
Se eliminarmos a segunda chamada recursiva e
trocarmos o if por um while, teremos uma verso
inteiramente equivalente:
voidquicksrt(intv[],intp,intr){
intj;
while(p<r){
j=separa(v,p,r);

quicksrt(v,p,j1);
p=j+1;
}
}

Exerccios 3
1. Que acontece se trocarmos p<r por p!=r na linha 2 do quicksort?
Que acontece se trocarmos j1 por j na linha 4 do cdigo? Que
acontece se trocarmos j+1 por j na linha 5?
2. PEGADINHA. Quais so os invariantes da funo quicksort?
3. Submeta o vetor 77553399 indexado por 1..4
funo quicksort. Teremos a seguinte sequncia de chamadas da
funo:
4. quicksort(v,1,4)
5. quicksort(v,1,2)
6. quicksort(v,1,0)
7. quicksort(v,2,2)
8. quicksort(v,4,4)

(observe a indentao). Repita o exerccio com o vetor 55442211


6633 indexado por 1..6.
9. Escreva uma funo com prottipo quick_sort(intv[],intn) que
rearranje um vetor v[0..n1] em ordem crescente. (Basta
invocarquicksort da maneira correta.)
10.A funo quicksort produz uma ordenao estvel do vetor?
11.Discuta a seguinte implementao do algoritmo Quicksort (supondo p
<r):
12. voidqcksrt(intv[],intp,intr){
13. intj;
14. j=separa(v,p,r);
15. if(p<j1)qcksrt(v,p,j1);
16. if(j+1<r)qcksrt(v,j+1,r);
17. }

18.FORMULAO ALTERNATIVA DA SEPARAO. Suponha dada uma funo que


resolve a primeira das formulao do problema da separao
dadas acima (ou seja, rearranja v[p..r] e devolve j tal que v[p..j]?
v[j+1..r].) Escreva uma verso do algoritmo Quicksort que use essa
funo de separao. Que restries devem ser impostas sobre j?
19.VERSO ITERATIVA. Escreva uma verso no recursiva do algoritmo
Quicksort. [Soluo.]
20.QUICKSORT ALEATORIZADO. Escreva uma verso
aleatorizada (= randomized) do algoritmo Quicksort.
21.TESTES COM VETOR ALEATRIO. Escreva um programa que teste,
experimentalmente, a correo de sua implementao do algoritmo
Quicksort. Use permutaes aleatrias de 1..n para os testes.
Compare o resultado da ordenao com 1..n.

Animaes do Quicksort
Animao do Quicksort, de Mike Bostock (veja
tambm uma visualizao esttica, uma variante,
e uma outra forma de visualizao)
Sorting Algorithms Animation, de David R. Martin
Demonstrao animada do Quicksort, copiada do
stio de R. Sedgewick (Universidade de Princeton)
Animao de algoritmos de ordenao, de Nicholas
Andr Pinho de Oliveira
Quick-sort com dana folclrica hngara,
Universidade Sapientia (Romnia)

Desempenho do Quicksort bsico


O consumo de tempo da funo quicksort
proporcional ao nmero de comparaes entre
elementos do vetor. Se o ndice jdevolvido
por separa estiver sempre mais ou menos a meio
caminho entre p e r, o nmero de comparaes ser
aproximadamente n log n, sendo n o tamanho rp+1 do

vetor. Por outro lado, se o vetor j estiver ordenado ou


quase ordenado, o nmero de comparaes ser
aproximadamente
n2.

Portanto, o pior caso do quicksort no melhor que o


dos algoritmos elementares. Felizmente, o pior caso
muito raro: em mdia, o consumo de tempo
do quicksort proporcional a
n log n .

(Veja detalhes na minha pgina Anlise do Quicksort.)

Exerccios 4
1. CRONOMETRAGEM. Escreva um programa que cronometre sua
implementao do Quicksort (use a funo clock da biblioteca time).
Divida os tempos por n logn para comparar com as previses
tericas.
2. VERSO TRUNCADA. Escreva uma verso do algoritmo Quicksort
com cutof para vetores pequenos: quando o vetor a ser ordenado
tiver menos que M elementos, a ordenao passa a ser feita
pelo algoritmo de insero. O valor de M pode ficar entre 10
e 20. (Esse truque usado na prtica porque o algoritmo de
insero mais rpido que o Quicksort puro quando o vetor
pequeno. O fenmeno muito comum: algoritmos sofisticados so
tipicamente mais lentos que algoritmos simplrios quando o volume
de dados pequeno.)
3. [Pedro Rey] A seguinte funo promete rearranjar o vetor v[p..r] em
ordem crescente. Mostre que a funo est correta. Estime o
consumo de tempo da funo.
4. voidpsort(intv[],intp,intr){
5. if(p>=r)return;
6. if(v[p]>v[r]){
7. intt;
8. t=v[p],v[p]=v[r],v[r]=t;}
9. psort(v,p,r1);
10. psort(v,p+1,r);}

Altura da pilha de execuo do Quicksort


Na verso bsica do Quicksort, o cdigo cuida
imediatamente do segmento v[p..j1] e trata do
segmento v[j+1..r] somente depois que v[p..j
1] estiver ordenado. Dependendo do valor de j nas
sucessivas chamadas da funo, a pilha de
execuo pode crescer muito, atingindo altura igual ao
tamanho do vetor. (Isso acontece, por exemplo, se o
vetor estiver em ordem decrescente.) Esse fenmeno
pode esgotar o espao de memria. Para controlar o
crescimento da pilha de execuo preciso tomar duas
providncias:
1. cuidar primeiro do menor dos segmentos v[p..j
1] e v[j+1..r] e
2. eliminar a segunda chamada recursiva da funo
(trocando o if por um while) como j fizemos
acima.
Se adotarmos essas providncias, o tamanho do
segmento que est no topo da pilha de execuo ser
menor que a metade do tamanho do segmento que
est logo abaixo na pilha. De modo mais geral, o
segmento que est em qualquer das posies da pilha
de execuo ser menor que metade do segmento que
est imediatamente abaixo. Assim, se a funo for
aplicada a um vetor com n elementos, a altura da pilha
no passar de log n.
//Afunorearranjaovetorv[p..r]

//emordemcrescente.

void

quickSort(intv[],intp,intr)

intj;

while(p<r){

j=separa(v,p,r);

if(jp<rj){

quickSort(v,p,j1);

p=j+1;

}else{

quickSort(v,j+1,r);

r=j1;

Exerccios 5
1. Suponha que a seguinte funo aplicada a um vetor
com n elementos. Mostre que a altura da pilha de execuo pode
passar de por log n.
2. voidquicks(intv[],intp,intr){
3. intj;
4. if(p<r){
5. j=separa(v,p,r);
6. if(jp<rj){
7. quicks(v,p,j1);

8. quicks(v,j+1,r);}
9. else{
10. quicks(v,j+1,r);
11. quicks(v,p,j1);}}}

12.Familiarize-se com a funo qsort da biblioteca stdlib.

APNDICE: outras formas do Quicksort


O algoritmo Quicksort pode ser implementado de vrias
outras maneiras alm da discutida acima. A diferena
entre as vrias verses est no modo de resolver o
problema da separao. (Em todas as verses abaixo,
o mecanismo de controle da altura da pilha de
execuo foi omitido para tornar o cdigo mais legvel.)
1. A verso abaixo usa uma formulao do problema
da separao quase igual que adotamos acima:
rearranja v[p..r] de modo que tenhamos v[p..j
1]?v[j]?v[j+1..r] (note o segundo ?) para
algum j em p..r.
2. //Afunorearranjaovetorv[p..r]
3. //emordemcrescente.
4.
5. voidquick_FlipFlop(intv[],intp,intr)
6. {
7. intj,k,t,ff;
8. if(p<r){
9. j=p,k=r;
10. ff=1;
11. while(j<k){

12. if(v[j]>v[k]{
13. t=v[j],v[j]=v[k],v[k]=t;
14. ff=ff;
15. }
16. if(ff==1)k;
17. else++j;
18. }
19. quick_FlipFlop(v,p,j1);
20. quick_FlipFlop(v,j+1,r);
21. }
22. }

23.
A verso seguinte (veja livro de Cormen,
Leiserson e Rivest) usa a primeira das duas
formulaes do problema da separao
mencionadas acima: rearranja v[p..r] de modo
que v[p..j]?v[j+1..r] para algum j em p..r1.
24. //Afunorearranjaovetorv[p..r]
25. //emordemcrescente.
26.
27. voidquick_CLR(intv[],intp,intr)
28. {
29. intc,i,j,t;
30. if(p<r){
31. c=v[p];
32. i=p1,j=r+1;
33. while(1){
34. doj;while(v[j]>c);
35. do++i;while(v[i]<c);

36. if(i>=j)break;
37. t=v[i],v[i]=v[j],v[j]=t;
38. }
39. quick_CLR(v,p,j);
40. quick_CLR(v,j+1,r);
41. }
42. }

43.
Eis outra verso (se no me engano, ela est
no livro de Sedgewick). Exerccio: formule com
preciso o problema da separao que essa verso
resolve.
44. //Afunorearranjaovetorv[p..r]
45. //emordemcrescente.
46.
47. voidquick_S(intv[],intp,intr){
48. intc,i,j,t;
49. if(p<r){
50. c=v[(p+r)/2];
51. i=p,j=r;
52. while(i<=j){
53. while(v[i]<c)++i;
54. while(c<v[j])j;
55. if(i<=j){
56. t=v[i],v[i]=v[j],v[j]=t;
57. ++i,j;
58. }
59. }

60. quick_S(v,p,j);
61. quick_S(v,i,r);
62. }
63. }

64.
A seguinte verso do Quicksort sugerida
no livro de Sedgewick. Exerccio: formule com
preciso o problema da separao que essa verso
resolve.
65. #definetroca(A,B){intt=A;A=B;B=t;}
66. voidquick_S2(intv[],intp,intr){
67. if(p<r){
68. inti=p1,j=r,c=v[r];
69. while(1){
70. while(v[++i]<c);
71. while(c<v[j])if(j==p)break;
72. if(i>j)break;
73. troca(v[i],v[j]);
74. }
75. troca(v[i],v[r]);
76. quick_S2(v,p,j);
77. quick_S2(v,i+1,r);
78. }
79. }

Mediana e i-simo menor elemento

Este pgina inspirada no captulo 10 de CLR. Ela trata do


problema da mediana e tambm do problema mais geral
do i-simo menor elemento de um conjunto.
Mediana

Dado um conjunto S de nmeros, o i-simo menor elemento


de S o nmero x em S tal que o conjunto {s S : s x}
tem exatamentei elementos. Se s1,s2,,sn uma
enumerao dos elementos de S tal que s1 < s2 < < sn,
ento o i-simo menor elemento si. Por exemplo, o 1simo menor elemento de S o mnimo de S e o |S|-simo
menor elemento o mximo de S.
A mediana de um conjunto S com n elementos o
(n+1)/2-simo (veja definio de piso) menor elemento
de S. (Eu poderia ter definido a mediana como o (n+1)/2simo menor elemento. Isso s faria diferena
quando n fosse par.) (No confunda mediana com mdia.
Por exemplo, a mdia de {1,2,99} 51, enquanto a
mediana 2.)
Nossos conjuntos sero representados por vetores cujos
elementos so distintos dois a dois. ( bem verdade que
quase tudo se aplica igualmente bem a vetores com
elementos repetidos, mas a discusso fica mais simples se
os elementos forem distintos dois a dois.) Nesse contexto,
as definies podem ser reescritas como segue.
O i-simo menor elemento de um vetor estritamente
crescente A[1..n] o nmero A[i]. De modo um pouco mais
geral, o i-simo menorelemento de um vetor estritamente

crescente A[p..r] o nmero A[ip+1]. De modo ainda


mais geral, o i-simo menor elemento de um vetor A[p..r]
sem elementos repetidos o nmero x tal que x = A[k]
para algum k e |{ j : A[j] x}| = i . Em particular,
a mediana de um vetor A[p..r] sem elementos repetidos
o (n+1)-simo menor elemento do vetor.
PROBLEMA DA MEDIANA: Encontrar a mediana de um
vetor A[p..r] sem elementos repetidos.
PROBLEMA DA MEDIANA GENERALIZADA: Dado um vetor A[p..r]
sem elementos repetidos e um nmero natural i,
encontrar o i-simo menor elemento do vetor.
claro que o problema tem soluo se e somente se
1 i rp+1.
muito fcil resolver o problema em tempo (n lg n),
sendo n o nmero de elementos do vetor. O desafio est
em obter um algoritmo mais rpido.
33

44

55

77

95

99

22

25

41

66

88

89

O quarto menor elemento do vetor 41 e a mediana 55.


Exerccios

1. Escreva um algoritmo que consuma tempo (n) para


determinar o segundo menor elemento de um vetor
com n 2 elementos. Escreva um algoritmo que
consuma tempo (n) para determinar o n1-simo
menor elemento de um vetor com n 2 elementos.
2. Encontrar o menor e o segundo menor elemento de
um vetor A[1..n] usando apenas n+lg n2
comparaes entre elementos do vetor (veja
a definio de teto).
3. Prove que (n+1)/2 = n/2.

4. Suponha que os elementos do vetor A[1..n] so


distintos dois a dois. Critique a seguinte definio
alternativa de mediana: A medianade A[1..n]
A[n/2].
5. Suponha que o vetor A[1..n] estritamente crescente.
razovel dizer que a mediana de A[1..n] A[n/2]?
razovel dizer que a mediana de A[1..n] n/2?
6. Suponha que caiu do cu um algoritmo que calcula a
mediana de um vetor com n elementos em (n)
unidades de tempo. Como esse algoritmo poderia ser
usado para melhorar a qualidade do
subalgoritmo SEPARE a ser usado no QUICKSORT?
Seleo aleatorizada

Existem algoritmos que resolvem o problema da mediana


generalizada em tempo (n), mesmo no pior caso. Nesta
pgina, vamos nos contentar com algo mais fraco mas
muito prtico: um algoritmo cujo consumo de
tempo esperado (n). Tal como o Quicksort, o algoritmo
usa o paradigma da diviso e conquista.
O algoritmo recebe um vetor A[p..r] sem elementos
repetidos e um ndice t no conjunto {p,,r} e usa o SEPAREALEATORIZADO para encontrar o (tp+1)-smo menor
elemento do vetor:
SELECIONE-ALEAT (A, p, t, r)
1 se p = r
2 ento devolva A[p]
3 seno q SEPARE-ALEATORIZADO (A, p, r)
5 se q t
6 ento devolva SELECIONE-ALEAT (A, p, t, q)
7 seno devolva SELECIONE-ALEAT (A, q+1, t, r)

(Observe que na linha 6 as condies para a invocao


de SELECIONE-ALEAT esto satisfeitas. De fato, A[p..q] tem

menos elementos queA[p..r] e t pertence ao conjunto {p,


,q}. Algo anlogo acontece na linha 7.)
Prova da correo do algoritmo: Seja n = rp+1. Se n = 1
ento temos t = p e portanto o algoritmo devolve a
resposta correta na linha 2. Agora suponha que n > 1.
Depois da linha 3 temos A[p..q] A[q+1..r].
Se t q ento o (tp+1)-simo menor elemento
de A[p..r] o (tp+1)-simo menor elemento
de A[p..q]. Nesse caso, podemos supor, como
hiptese de induo, que a invocao de SELECIONEALEAT na linha 6 devolve a resposta correta.
Se t > q ento o (tp+1)-simo menor elemento
de A[p..r] o (tq)-simo menor elemento
de A[q+1..r]. Nesse caso, podemos supor, como
hiptese de induo, que a invocao de SELECIONEALEAT na linha 7 devolve a resposta correta.
Exerccios

1. [VERSO ITERATIVA] Escreva uma verso no recursiva


do algoritmo SELECIONE-ALEAT.
2. Suponha dado um algoritmo SEPAR que faa o
seguinte: ao receber um vetor A[p..r], devolve um
ndice q no intervalo p..r depois de rearranjar o vetor
de tal modo que A[p..q1] A[q] < A[q+1..r].
Escreva uma verso do SELECIONE-ALEAT que use o
algoritmo SEPAR no lugar de SEPARE.
Consumo de tempo da seleo aleatorizada

Suponha que SELECIONE-ALEAT aplicado a um vetor


com n elementos distintos dois a dois. Seja E(n) o consumo
de tempo do esperado do algoritmo. Ento
E(n) est em (n) .
A prova deste fato semelhante ao clculo do consumo de
tempo esperado do QUICKSORT-ALEATORIZADO. Se SEPAREALEATORIZADO consomen unidades de tempo e as demais

operaes consomem 1 unidade de tempo, E satisfaz


a recorrncia
E(n) = 1 + n +

E(max(1,n1)) + 0<i<n E(max(i,ni))


n

e portanto temos E(n) 1+n + (E(n1) + 2 i E(i)) / n ,


com soma indo de i = n/2 a i = n1. Segue da
que E(n) = (n) .
Exerccios

1. Troque SEPARE-ALEATORIZADO por SEPARE no algoritmo


acima. Quanto tempo o algoritmo resultante consome
no pior caso?
2. Seja T(n) o consumo de tempo do algoritmo SEPAREALEATORIZADO no pior caso. Supondo que cada uma das
linhas 1, 2, 4 e 5 consome 1 unidade de tempo e que
linha 3 consome n unidades de tempo, escreva a
recorrncia que T satisfaz. Em seguida, prove que T(n)
est em (n2).
3. Suponha que o algoritmo SEPARE-ALEATORIZADO recebe
vetores arbitrrios (possivelmente com elementos
repetidos). O consumo de tempo esperado do
algoritmo est em (n) nesse caso?
4. [CLRS 9.3-5] Suponha dado um algoritmo MEDIANA,
com parmetros (A,p,r), que rearranja os elementos de
um vetor A[p..r] de nmeros inteiros e devolve um
ndice q tal
que p q r e A[p..q1] A[q] A[q+1..r] e A[q]
a mediana de A[p..r]. Suponha ainda que o consumo
de tempo do algoritmo linear, ou seja, (n),
sendo n = rp+1. Escreva um algoritmo que receba
um vetor A[p..r] de inteiros e um ndice i no conjunto
{1,,n} e devolva o valor do i-simo menor elemento
de A[p..r]. O seu algoritmo deve utilizarMEDIANA como
subrotina e deve consumir tempo (n).
Um algoritmo assintoticamente melhor, mesmo no pior caso

Existe um algoritmo que resolve o problema da mediana


generalizada em tempo (n), mesmo no pior caso. Mas o
algoritmo um tanto complexo.
Exerccios

1. [CLRS 9.3-7] Dado um vetor A[1..n] de nmeros


naturais e um nmero natural k n, determinar
os k elementos do vetor que esto mais prximos da
mediana do vetor. Escreva um algoritmo que resolva o
problema em tempo (n).

bc1435 Mediana
Publicado em 28/05/2013

O problema de determinar a mediana de um vetor qualquer com

inteiros descobrir o

-simo menor elemento do vetor, isto , o

elemento da posio

quando consideramos o vetor ordenado.

Uma soluo fcil ordenar o vetor, sabemos que isso consome

instrues no pior caso. Sabemos encontrar o menor elemento e maior


elemento em tempo
. possvel fazer melhor no caso da

mediana?
Vamos enunciar o problema de um modo mais genrico:
Problema da Seleo

Instncia: Uma sequncia

inteiro

de

inteiros (distintos) e um

Resposta: O elemento

outros elementos de

que maior do que exatamente

Uma soluo para o problema da seleo resolve, claramente, o


problema da mediana. Curiosamente, se temos uma soluo para o
problema da mediana, tambm temos uma soluo para o problema da
seleo.
Exerccio 1 Suponha que Mediana (A[1..n]) um algoritmo que devolve
a posio da mediana do vetor
. Escreva um algoritmo que usa

Mediana para resolver o problema da seleo.


Uma soluo elegante para o problema da mediana usa o algoritmo
de partio doquicksort. Lembremos que
Partio

Dadoumvetor

denmerosinteiros,com

Devolve

eoselementosde

rearranjadosdemodoque

se

dado por Partio a mediana ento estamos feitos;

se

maior que a mediana ento continuamos em

continuamos pela procura do

seno

-simo menor elemento de

Podemos generalizar essa soluo para o problema da seleo


SeleoPartio(A[1..n],k)

DadoumvetorA[1..n]denmerosinteiroseuminteirokentre1en
DevolveoksimomenorelementodeA.

1Se(n=1)devolva(A[1])
2Seno{
3d<Partio(A[1..n])
4Se(k<d)
5devolvaSeleoPartio(A[1..d1],k)
6SenoSe(k>d)
7devolvaSeleoPartio(A[d+1..n],kd)
8Senodevolva(A[p])}

Esse algoritmo tem custo quadrtico no pior caso. Vamos contar o


nmero de comparaes feitas com uma entrada de temanho

no

pior caso. Notemos que o nmero de comparaes proporcional ao


nmero de instrues bsicas que o algoritmo executa.
No pior caso,

na linha 3 sempre a primeira posio do vetor (

) de modo que o nmero de compraes

, portanto

Esse custo pode ser melhorado se conseguirmos escolher bem um piv


de modo que a partio seja mais equilibrada. De fato se o tamanho do
subproblema na recurso for

para alguma constante

ento
, portanto

De fato, iterando a recurrncia

e tomando

da ordem de

vezes

temos

, portanto

Um modo de escolher o piv que podemos adotar como a que foi feita
no quicksortprobabilstico:
escolhemos
um
elemento
do
vetor
aleatoriamente para ser o piv. Tal piv bom se no for dos 10%
menores elementos do vetor nem dos 10% maiores elementos do vetor.
A probabilidade de sortear um piv bom

e o nmero esperado

de sorteios de pivs no-bons entre dois bons consecutivos no

mximo

. Se todas as escolhas consecutivas so boas ento a

cada pivotamento pelo menos 10% do vetor descartado logo o nmero


de comparaes executadas

logo

(verifique usando induo).

Seleo_probabilstico
Seleo_probabilstico(A[1..n],k)

DadosumvetordeinteirosA[1..n]uminteirokentre1en.
DeterminaoksimomenorelementodeA.

1Se(n=1)devolva(A[1])
2Seno{
3sorteierem[1..n];A[r]<>A[i]
4p<Partio(A[1..n])
5Se(k<=p)
6devolva(Seleo_probabilstico(A[1..p],k))
7Seno
8devolva(Seleo_probabilstico(A[p+1..n],pk))}

Algoritmo determinstico de complexidade linear


O algoritmo abaixo determina a mediana de um vetor em tempo linear e
foi descoberto porManuel Blum, Bob Floyd, Vaughan Pratt, Ron Rivest,
and Bob Tarjan no incio dos anos 70 [ Linear time bounds for median
computations].
IdeiageraldoalgoritmoSELEO:

1.Dividaosnelementosdaentradaem

gruposdecinco

elementoscada(enomximoumgrupoconsistindodoselementos
restantes);

2.Descubraamedianaemcadaumdos

grupos;

3.UseSELEOrecursivamenteparadescobriramedianaA[mdm]das
medianas;

4.ParticioneaentradausandoA[mdm]comopiv;

5.Sek<=mdmuseSELEOrecursivamenteemA[1..mdm]paraacharok
simomenor;

6.SenouseSELEOrecursivamenteemA[mdm..n]paraacharo(kmdm)
simomenor.

Detalhamento:
Seleo
Seleo(A[1..n],k)

DadoumvetorA[1..n]einteiroseuminteirokentre1en
DevolveoksimomenorelementodeA

1.Se(n<=25)useforabruta;
2.Seno{
3.m<

n/5

4.Paraide1atm
B[i]<Seleoforabruta(A[5i4..5i],3)
6.mdm<Seleo(B[1..m],
m/2
)
7.A[1]<>A[mdm]
8.d<Partio(A[1..n])
9.Se(k<d)Devolva(Seleo(A[1..d1],k))
10.SenoSe(k>d)Devolva(Seleo(A[d+1..n],kd))
10.SenoDevolva(A[mdm])}.

A ideia fundamental para a eficincia dessa algoritmo que os dois


subvetores das instncias recursivas no sero nem muito grande. A
mediana-das-medianas (
) maior do que
medianas, e

cada uma desses medianas maior do que dois outros elementos em


seu grupo.

Em outras palavras, a mediana-das-medianas maior do que pelo


menos
do vetor de entrada.

Simetricamente,

menor do que, pelo menos

elementos

da entrada. Assim, no pior caso, a chamada recursiva final e sobre um


vetor de tamanho
.

Determinar a mediana de cada grupo usa no mximo

comparaes e a partio usa no mximo

o total de no mximo

comparaes portanto,

comparaes mais as comparaes feitas

nas chamadas recursivas, i.e., o nmero de comparaes para entradas


de tamanho

no pior caso satisfaz

cuja soluo

Mximo e mnimo
Determinar o mnimo de um sequncia

mais
fcil
exatamente

que

de

inteiros

Seleo(A[1..n],1),
pode
ser
feito
usando
comparaes:examinar cada elemento do conjunto,

e armazenar o menor elemento visto at agora.

Encontrar o mximo tambm realizado com

comparaes.

este o melhor que podemos fazer? Sim, o seguinte argumento


estabelece um limitante inferior de
comparaes para o problema

de determinar o mnimo: modelamos o comportamento de qualquer


algoritmo que determina o mnimo como um torneio entre os elementos.
Cada comparao
uma partida no torneio em que o menor dos

dois elementos ganha. A observao importante que todos os


elementos, exceto o vencedor deve perder pelo menos um jogo.
Assim,
comparaes so necessrias para determinar o mnimo.

Quantas comparaes so necessrias para determinar o mximo e o


mnimo de uma sequncia

de

nmeros inteiros?

Exerccio 2 Descreva um algoritmo que, no pior caso, determina o


mximo e o mnimo de uma sequncia de

nmeros,

simultaneamente, e que executa no mximo

Exerccio 3 Mostre que

comparaes.

comparaes so necessrias no pior

caso para encontrar tanto o mximo e o mnimo de

nmeros.

(Dica: Considere quantos nmeros so potencialmente ou o mximo ou


mnimo, e investigue como uma comparao afeta essas contagens.)

Exercico 4 Mostre que o segundo menor de

ser encontrado com

elementos pode

comparaes no pior caso.