Escolar Documentos
Profissional Documentos
Cultura Documentos
Ordenação
Ordenação
Mergesort e Quicksort
Felipe P.G. Bergo
Nesta aula apresentamos dois algoritmos de ordenao recursivos: mergesort e quicksort. Ambos ca tentam reduzir o problema de ordenao a dois subproblemas de tamanhos similares, levando a ca uma performance proporcional a nlog(n) em caso de sucesso.
Mergesort
Mergesort (ordenao por intercalao) divide o vetor de entrada em dois subvetores com metade ca ca do tamanho do vetor original (em caso de tamanho mpar, um dos subvetores ter um elemento a a mais que o outro). Cada um dos subvetores ordenado recursivamente. Os dois subvetores so e a intercalados em um vetor temporrio. Mergesort garante que os dois subproblemas tm tamanho a e n ca o a e 2 , mas requer alocao de memria para o vetor temporrio. O algoritmo do Mergesort dado abaixo: Algoritmo 1 MergeSort
Entrada: Sa da: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. Vetor V , ndice inicial a, ndice nal b. Vetor V , ordenado.
Se a = b, retorne V . Compute m (a+b) . 2 Ordene V [a . . . m] por MergeSort. Ordene V [m + 1 . . . b] por MergeSort. Faa i a, j m + 1 e k 0. c Aloque um vetor T [0 . . . (b a)]. Enquanto i < m + 1 ou j < b + 1 Faa c Se (i m e V [i] < V [j]) ou (j = b + 1) Ento a Faa T [k] V [i], incremente k e incremente i c Seno a Faa T [k] V [j], incremente k e incremente j. c Para i = a at b Faa e c V [i] T [i a]. Desaloque T . Retorne V .
void mergesort(int *v,int inicio,int fim) { int i,j,k,m,*t; if (inicio==fim) return; // ordenacao recursiva das duas metades m = (inicio+fim)/2; mergesort(v,inicio,m); mergesort(v,m+1,fim); // intercalacao no vetor temporario t i = inicio; j = m+1; k = 0; t = (int *) malloc(sizeof(int) * (fim-inicio+1)); while(i<m+1 || j<fim+1) { if (i==m+1) { // i passou do final da primeira metade, pegar v[j] t[k] = v[j]; j++; k++; } else if (j==fim+1) { // j passou do final da segunda metade, pegar v[i] t[k] = v[i]; i++; k++; } else if (v[i] < v[j]) { // v[i]<v[j], pegar v[i] t[k] = v[i]; i++; k++; } else { // v[j]<=v[i], pegar v[j] t[k] = v[j]; j++; k++; } } // copia vetor intercalado para o vetor original for(i=inicio;i<=fim;i++) v[i] = t[i-inicio]; free(t); }
Quicksort
O algoritmo Quicksort no requer armazenamento temporrio, mas seu procedimento de partio a a ca pode gerar subvetores de tamanhos diferentes. Quicksort escolhe um elemento pivot, e particiona o vetor de tal forma que todos os elementos menores que o pivot quem ` esquerda, e todos os a elementos maiores ou iguais ao pivot quem ` direita. As duas parties so ordenadas recursivaa co a 2
mente, e o resultado o vetor ordenado. No pior caso, quicksort leva tempo n2 . No caso mdio, e e nlog(n). O quicksort pode ser expresso pelos dois algoritmos abaixo: Algoritmo 2 QuickSort
Entrada: Sa da: Vetor V , ndice inicial a, ndice nal b. Vetor V , ordenado.
Algoritmo 3 Particiona
Entrada: Sa da: Vetor V , ndice inicial a, ndice nal b. q, ndice do ponto de partio. ca
1. Faa x V [a], i a 1 e j b + 1. c 2. Enquanto verdadeiro Faa c 3. Repita j j 1 at que V [j] x e 4. Repita i i + 1 at que V [i] x e 5. Se i < j Ento a 6. Troque V [i] com V [j]. 7. Seno retorne q = j. a
E as funes abaixo implementam o quicksort em C: co int partition(int *v,int inicio,int fim) { int x,i,j,t; x = v[inicio]; i = inicio - 1; j = fim + 1; for(;;) { do { j--; } while(v[j]>x); do { i++; } while(v[i]<x); if (i<j) { t = v[i]; v[i] = v[j]; v[j] = t; } else return j; 3
} } void quicksort(int *v,int inicio,int fim) { int q; if (inicio < fim) { q = partition(v,inicio,fim); quicksort(v,inicio,q); quicksort(v,q+1,fim); } }
Performance
Agora que os principais algoritmos de ordenao foram apresentados, mostramos uma comparao ca ca entre eles. A Figura 1a mostra comparaes entre os grcos das funes y = x (comportamento co a co linear), y = xlog(x) (comportamento log-linear) e y = x2 (comportamento quadrtico). Algoritmos a quadrticos so to piores que os lineares e log-lineares que as duas ultimas curvas cam praticaa a a mente ileg veis. A Figura 1b mostra as curvas linear e log-linear isoladamente. Note que a curva log-linear, visualmente, facilmente confundida com comportamento linear. e
40000 35000 30000 25000 20000 15000 10000 5000 0 0 50 100 N 150 200 T T Linear Log-Linear Quadratico 16000 14000 12000 10000 8000 6000 4000 2000 0 0 500 1000 N 1500 2000 Linear Log-Linear
(a)
(b)
Figura 1: Curvas t picas de tempo vs. tamanho para algoritmos lineares, log-lineares e quadrticos. a Para testar os algoritmos, foi escrito um programa que aloca um e sorteia um vetor de 5000000 elementos inteiros aleatrios. Para diversos valores de n entre 0 e 5000000, cada algoritmo ordena o o maior nmero poss de subvetores distintos com n elementos. Para os algoritmos quadrticos u vel a executamos apenas at n = 50000, por limitao de tempo. O vetor inicial foi sorteado apenas e ca uma vez e todos os algoritmos tiveram que ordenar os mesmos dados (cada algoritmo trabalhou sobre uma cpia do vetor original). Testamos duas verses do quicksort: Quick Sort 1 usa sempre o o o primeiro elemento do sub-vetor como piv. Quick Sort 2 usa a mediana dos 3 primeiros elementos. o O cdigo-fonte do programa est dispon como perf.c na pgina do curso. A Figura 2 mostra o a vel a os resultados, obtidos em um Athlon64 3200+ (2.0 GHz). Como esperado, Bubble Sort o pior dos algoritmos quadrticos. Na Figura 2a sequer conseguimos e a 4
T (segundos)
Bubble Sort Selection Sort Insertion Sort Merge Sort Quick Sort 1 Quick Sort 2
30000
40000
50000
10000
20000 N
30000
40000
50000
(a)
2 1.8 1.6 T (segundos) 1.4 1.2 1 0.8 0.6 0.4 0.2 0 0 1e+06 2e+06 N 3e+06 4e+06 5e+06 0 0 1000 Merge Sort Quick Sort 1 Quick Sort 2 T (segundos) 0.06 0.05 0.04 0.03 0.02 0.01
(b)
Bubble Sort Selection Sort Insertion Sort Merge Sort Quick Sort 1 Quick Sort 2
2000 N
3000
4000
5000
(c)
(d)
ver os resultados do Merge Sort e Quick Sort, devido ` diferena de performance entre algoritmos a c quadrticos e log-lineares. As Figuras 2b e 2c mostram Merge Sort e Quick Sort isoladamente. a Note que o uso da mediana de 3 no teve efeito no Quick Sort para dados aleatrios. Em algumas a o situaes, com dados viciados, o uso da mediana poderia evitar comportamento quadrtico do co a Quick Sort. A Figura 2d mostra a performance para n < 5000: para pequenos vetores, a escolha de um algoritmo ruim vai fazer seu programa perder apenas alguns dcimos de segundo, e razovel e e a escolher um algoritmo ruim porm mais simples de implementar. A Figura 2a deve deixar claro e que, para vetores grandes, qualquer algoritmo log-linear muito melhor que qualquer algoritmo e quadrtico. E a Figura 2b mostra que, embora Mergesort garanta log-linearidade, na prtica o a a Quicksort mais eciente. e A Tabela abaixo mostra o nmero de linhas (excluindo linhas em branco e de comentrio) usadas u a na implementao de cada algoritmo. ca Algoritmo Bubble Sort Selection Sort Insertion Sort Merge Sort Quick Sort 1 Quick Sort 2 Linhas 9 12 10 27 20 26