Download - Análise empírica de algoritmos de ordenação
UNIVERSIDADE FEDERAL DO ABC
ANDRÉ RICARDO FREDERICO
ORLANDO DA SILVA JUNIOR
ANÁLISE EMPÍRICA DE ALGORITMOS DE ORDENAÇÃO
Santo André
Maio de 2012
ANDRÉ RICARDO FREDERICO
ORLANDO DA SILVA JUNIOR
ANÁLISE EMPÍRICA DE ALGORITMOS DE ORDENAÇÃO
Trabalho apresentado à Universidade
Federal do ABC como parte para
aprovação no curso de Análise de
Algoritmos e Estrutura de Dados,
ministrado pelo Prof. Dr. André Balan e
Prof. Dr. Daniel Martin.
Santo André
Maio de 2012
2
SUMÁRIO
1 INTRODUÇÃO ..................................................................................................................... 3
1.1 Objetivos ............................................................................................................................... 3
2 METODOLOGIA .................................................................................................................. 4
2.1 Experimentos ........................................................................................................................ 4
3 RESULTADOS ...................................................................................................................... 6
3.1 Bubble Sort ........................................................................................................................... 6
3.2 Selection Sort ........................................................................................................................ 7
3.3 Insertion Sort ........................................................................................................................ 8
3.4 Quick Sort ............................................................................................................................. 9
3.5 Heap Sort ............................................................................................................................ 10
3.6 Merge Sort .......................................................................................................................... 12
3.7 Shell Sort ............................................................................................................................ 12
3.8 Shell Sort – Knuth .............................................................................................................. 13
3.9 Shell Sort – Pardons ........................................................................................................... 14
3.10 Ordenação Inversa ............................................................................................................ 14
ANÁLISE DOS RESULTADOS E CONCLUSÃO ............................................................. 16
REFERÊNCIAS BIBLIOGRÁFICAS ................................................................................. 18
3
1 INTRODUÇÃO
Um algoritmo consiste em um procedimento com um conjunto regras não ambíguas
que especificam, para cada entrada, uma sequência finita de operações, resultando em uma
saída correspondente (TOSCANI; VELOSO, 2002). Assim, analisar um algoritmo significa
prever os recursos que o algoritmo necessitará (CORMEN et al., 2002).
A previsão de recursos necessários, neste trabalho, é obtida através da análise empírica
do tempo de execução de cada algoritmo. Com esta metodologia, espera-se obter o tempo
médio de um algoritmo para diferentes tamanhos de entrada. Esse tipo de avaliação permite
comparar a praticidade com que os algoritmos podem ser utilizados. No entanto, para que o
modelo matemático que avalia a complexidade de algoritmos não se torne inútil, também é
necessária a sua presença nessa avaliação.
É indiscutível a importância dos algoritmos de ordenação na ciência da computação
(CORMEN et. al, 2002; MCCONNELL, 2006). Neste trabalho, os principais algoritmos de
ordenação são avaliados e comparados.
Basicamente, um algoritmo de ordenação é aquele que resolve um problema de
ordenação. Formalmente, um problema de ordenação pode ser definido como uma sequência
de números ⟨ ⟩ cuja saída é uma permutação da sequência de entrada, tal que
(CORMEN et al., 2002).
1.1 Objetivos
O objetivo principal deste trabalho é estudar empiricamente a complexidade de tempo
dos algoritmos de ordenação interna.
Para alcançar esse objetivo, outros objetivos específicos se tornam necessários. Para
este trabalho, os objetivos específicos são:
a) Estudar as características gerais e a complexidade de cada algoritmo utilizado;
b) Implementar em uma linguagem adequada todos os algoritmos utilizados, a fim de
que fatores externos de desempenho não prejudiquem a avaliação final e que a
leitura do código seja facilmente compreensível;
c) Analisar e comparar o tempo médio do algoritmo com sua complexidade temporal.
d) Realizar, analisar e comparar o desempenho entre os algoritmos avaliados.
4
2 METODOLOGIA
A metodologia utilizada neste trabalho seguiu as diretrizes propostas pela atividade.
Assim, foram utilizados sete algoritmos de ordenação sobre seis conjuntos de dados aleatórios
diferentes conforme a variação de suas sementes mais um conjunto extra inversamente
ordenado.
Os conjuntos de dados possuíam nove diferentes tamanhos. Cada um desses conjuntos,
escolhidos pela diretriz da atividade proposta, comportava, respectivamente, 10 mil, 30 mil,
90 mil, 270 mil, 810 mil, 2430 mil, 7290 mil, 21870 mil e 65610 mil elementos. No caso
deste trabalho, esses conjuntos agregaram apenas números inteiros, embora pudessem ser
utilizados outros tipos de dados.
As sequências utilizadas para a geração dos números aleatórios tiveram as sementes: 4,
81, 151, 1601, 2307 e 4207. Por fim, os algoritmos de ordenação utilizados foram divididos
em:
a) Ineficientes: Bubble sort, Selection sort e Insertion sort; e
b) Eficientes: Quick sort, Heap sort, Merge sort e Shell sort (com 3 variações: padrão,
Knuth e Pardons).
2.1 Experimentos
Ao todo, o projeto contabilizou nove métodos de ordenação, nove tamanhos diferentes
de conjunto de dados e seis sequências aleatórias, totalizando 486 ordenações. Na rodada
inversa, os nove algoritmos foram utilizados para prever o pior caso: ordenar um conjunto
inversamente ordenado.
Foram utilizados dois computadores para realizar este experimento. O primeiro
computador é um notebook com processador Intel Core 2 Duo 2.10 Ghz 32-bits, 4 GB de
memória RAM. O segundo computador é um notebook com processador Intel Core 2 Duo 2.0
Ghz 32-bits, 3 GB de memória. Enquanto o primeiro computador utilizou-se do sistema
operacional Windows 7, o segundo utilizou o Linux Ubutu 11.10.
Para a coleta dos dados e posterior análise, os resultados de ambos os computadores
foram aproveitados, sem, no entanto, intercalar as respostas dos algoritmos ou suas sequências
numéricas. Isto foi realizado para: (a) analisar a influência do computador sobre a execução
dos algoritmos em cada conjunto de dados e (b) acelerar os experimentos, que poderiam
demorar horas ou dias.
5
As funções de tempo próprias de cada sistema operacional foram implementadas e
utilizadas em seus respectivos ambientes.
6
3 RESULTADOS
Com os experimentos, notou-se que a partir do tamanho de entrada 2430 mil os
algoritmos ineficientes estavam levando mais de duas horas para serem concluídos. Por esta
razão, este trabalho optou por não continuar executando os algoritmos ineficientes para os
tamanhos maiores que 810 mil elementos e nem os apresentará nestes resultados.
Ainda, para evitar o pior caso do algoritmo Quick Sort, ele foi implementado
parcialmente em modo iterativo e a seleção do pivô se deu de maneira aleatória. Também,
para melhorar a execução do algoritmo Bubble Sort, sua implementação foi modificada de
modo a não executar mais de uma vez o que já foi executado.
Os resultados expressos nos gráficos abaixo apresentam a relação tamanho x tempo de
cada um dos algoritmos utilizados pelo projeto. Os resultados são apresentados por algoritmo
e semente utilizada. Por fim, é apresentado o resultado da rodada inversa.
3.1 Bubble Sort
O Bubble Sort é um algoritmo de ordenação interna que compara pares de elementos,
trocando aqueles que estão fora de ordem até que a lista esteja ordenada (MCCONNELL,
2006).
A Figura 1 apresenta a relação tamanho do conjunto x tempo médio, em
milissegundos, do algoritmo Bubble Sort para as seis sementes utilizadas. Os tamanhos
presentes no gráfico são 10 mil, 30 mil e 90 mil elementos. A Figura 2 apresenta a mesma
relação da Figura 1, mas apenas para os tamanhos 270 mil e 810 mil elementos.
7
Figura 1 – Gráfico comparativo do tempo médio do algoritmo
Figura 2 – Gráfico comparativo do tempo médio do algoritmo
3.2 Selection Sort
O Selection Sort é um dos algoritmos de ordenação mais simples que existe.
Basicamente, o algoritmo divide a lista em duas listas: ordenada e desordenada. Percorrendo a
lista desordenada, o algoritmo seleciona nela o menor valor e a insere na lista ordenada
(BALAN, 2012; SEDGWICK, 1990).
Assim como o Bubble Sort, o algoritmo Selection Sort está dividido em duas análises:
do tamanho 10 mil até o tamanho 90 mil (Figura 3) e do tamanho 270 mil até o tamanho 810
mil (Figura 4).
0
10000
20000
30000
40000
50000
60000
10000 30000 90000
Te
mp
o m
éd
io (
ms)
Bubble Sort
0
1000000
2000000
3000000
4000000
5000000
6000000
270000 810000
Te
mp
o m
éd
io (
ms)
Bubble Sort
8
Figura 3 – Gráfico comparativo do tempo médio do algoritmo
Figura 4 – Gráfico comparativo do tempo médio do algoritmo
3.3 Insertion Sort
A ideia por trás do Insertion Sort é ordenar uma lista através da inserção de elementos.
Se um elemento é inserido na lista, ele já deve ser colocado em sua posição correta
(MCCONNELL, 2006).
Assim como o Selection Sort, o algoritmo Insertion Sort também subdivide suas
análises no mesmo espaço de tamanho de conjuntos. A Figura 5 e a Figura 6 apresentam essas
análises.
0
10000
20000
30000
40000
50000
60000
70000
10000 30000 90000
Te
mp
o m
éd
io (
ms)
Selection Sort
0
1000000
2000000
3000000
4000000
5000000
6000000
270000 810000
Te
mp
o m
éd
io (
ms)
Selection Sort
9
Figura 5 – Gráfico comparativo do tempo médio do algoritmo
Figura 6 – Gráfico comparativo do tempo médio do algoritmo
3.4 Quick Sort
O Quick Sort é um dos melhor algoritmos de ordenação que existe. Recursivamente, o
algoritmo escolhe um elemento e divide a lista em duas partes: a primeira, com todos os
elementos menores que o elemento escolhido; e a segunda, com todos os maiores. Quando a
lista atinge o tamanho mínimo, o algoritmo, então, ordena e devolve o pequeno conjunto
ordenado (MCCONNELL, 2006; SEDGWICK, 1990).
O Quick Sort foi o primeiro algoritmo da categoria dos algoritmos eficientes utilizado.
Para apresentar seus resultados e dos demais algoritmos eficientes, duas análises foram
realizadas: a primeira abrange os seis primeiros tamanhos de conjuntos de dados (de 10 mil
0
2000
4000
6000
8000
10000
12000
14000
16000
10000 30000 90000
Te
mp
o m
éd
io (
ms)
Insertion Sort
0
200000
400000
600000
800000
1000000
1200000
1400000
1600000
270000 810000
Te
mp
o m
éd
io (
ms)
Insertion Sort
10
elementos a 2430 mil elementos) e a segunda abrange os três últimos conjuntos (de 7290 mil
elementos a 65610 mil elementos).
A primeira análise é apresentada na Figura 7, enquanto que a segunda análise é
apresentada na Figura 8.
Figura 7 – Gráfico comparativo do tempo médio do algoritmo
Figura 8 – Gráfico comparativo do tempo médio do algoritmo
3.5 Heap Sort
O Heap Sort é um algoritmo baseado na árvore binária Heap (máxima), onde para
cada subárvore o valor da raiz é maior que dos filhos. A ideia geral do Heap Sort é a
construção do Heap: o maior elemento será sempre a raiz da árvore; a raiz é copiada para a
0
100
200
300
400
500
600
700
800
900
10000 30000 90000 270000 810000 2430000
Te
mp
o m
éd
io (
ms)
Quick Sort
0
5000
10000
15000
20000
25000
7290000 21870000 65610000
Te
mp
o m
éd
io (
ms)
Quick Sort
11
última posição e, então, o Heap é reconstruído, até que a lista esteja ordenada
(MCCONNELL, 2006).
Para apresentar os resultados do Heap Sort, duas análises foram realizadas: a primeira
abrange os seis primeiros tamanhos de conjuntos de dados (de 10 mil elementos a 810 mil
elementos) e a segunda abrange os três últimos conjuntos (de 2430 mil elementos a 65610 mil
elementos).
Figura 9 – Gráfico comparativo do tempo médio do algoritmo
Figura 10 – Gráfico comparativo do tempo médio do algoritmo
0
100
200
300
400
500
600
700
800
10000 30000 90000 270000 810000
Te
mp
o m
éd
io (
ms)
Heap Sort
0
20000
40000
60000
80000
100000
120000
2430000 7290000 21870000 65610000
Te
mp
o m
éd
io (
ms)
Heap Sort
12
3.6 Merge Sort
O Merge Sort é baseado na ideia de que unir duas listas ordenadas é um processo
rápido. Sendo que uma lista com apenas um elemento já está ordenada, o Merge Sort quebra
as listas até que elas tenham esse tamanho único e depois as une, recursivamente.
Para o algoritmo Merge Sort, foi realizada apenas uma análise. Conforme se percebe
na Figura 11, os diferentes tamanhos não desbalancearam totalmente o gráfico, já que a faixa
de tempo não possui uma largura tão ampla de valores, como nos demais algoritmos
analisados.
Figura 11 – Gráfico comparativo do tempo médio do algoritmo
3.7 Shell Sort
O algoritmo Shell Sort é uma variação do algoritmo de inserção. A ideia por trás do
Shell Sort é inserir os elementos em suas posições corretas através de passos mais largos. O
Shell Sort utiliza uma sequência de incrementos que determina qual é o próximo elemento a
ser ordenado na subsequência (BALAN, 2012).
Para o algoritmo Shell Sort, quatro análises foram realizadas. Nas duas primeiras
análises, é verificado a faixas entre (a) 10 mil elementos e 810 mil elementos e (b) 2430 mil
elementos e 65610 mil elementos. Nas duas últimas análises, todas as faixas de valores são
comparadas, porém apenas nas versões de Knuth e Pardons, nos subcapítulos seguintes.
0
5000
10000
15000
20000
25000
30000
35000
40000
Te
mp
o m
éd
io (
ms)
Merge Sort
13
Essa divisão foi feita para que seja possível analisar o algoritmo como um todo,
independente de sua versão. Como é possível notar, os gráficos das versões de Knuth e
Pardons aproximam-se um do outro e da versão original do algoritmo Shell Sort.
A Figura 12 e a Figura 13 apresentam a análise gráfica do algoritmo Shell Sort em sua
versão original.
Figura 12 – Gráfico comparativo do tempo médio do algoritmo
Figura 13 – Gráfico comparativo do tempo médio do algoritmo
3.8 Shell Sort – Knuth
A Figura 14 apresenta a análise do algoritmo Shell Sort – versão Knuth.
0
100
200
300
400
500
600
700
800
900
10000 30000 90000 270000 810000
Te
mp
o m
éd
io (
ms)
Shell Sort
0
10000
20000
30000
40000
50000
60000
70000
80000
90000
100000
2430000 7290000 21870000 65610000
Te
mp
o m
éd
io (
ms)
Shell Sort
14
Figura 14 – Gráfico comparativo do tempo médio do algoritmo
3.9 Shell Sort – Pardons
A Figura 15 apresenta a análise do algoritmo Shell Sort – versão Pardons.
Figura 15 – Gráfico comparativo do tempo médio do algoritmo
3.10 Ordenação Inversa
Por fim, é apresentada nesta seção a rodada da ordenação inversa. Nesta atividade, os
algoritmos ordenaram um conjunto de dados com 50 mil elementos. Nesse conjunto, os
elementos foram inseridos em ordem decrescente sobre uma com distribuição linear.
0
10000
20000
30000
40000
50000
60000
70000
80000
90000
100000T
em
po
mé
dio
(m
s)
Shell Sort - Knuth
0
5000
10000
15000
20000
25000
30000
35000
40000
45000
50000
Te
mp
o m
éd
io (
ms)
Shell Sort - Pardons
15
A finalidade dessa rodada é avaliar os algoritmos na certeza de seus piores casos. A
Figura 16 apresenta a análise realizada nos algoritmos ineficientes e a Figura 17, nos
algoritmos eficientes.
Figura 16 – Gráfico comparativo da rodada inversa nos algoritmos ineficientes
Figura 16 – Gráfico comparativo da rodada inversa nos algoritmos eficientes
0
2000
4000
6000
8000
10000
12000
14000
16000
18000
20000
Bubble Sort Selection Sort Insertion Sort
Te
mp
o (
ms)
Ineficientes
14,4
14,6
14,8
15,0
15,2
15,4
15,6
15,8
16,0
16,2
Quick Sort Heap Sort Merge Sort Shell Sort Shell Sort -Knuth
Shell Sort -Pardons
Te
mp
o (
ms)
Eficientes
16
4 ANÁLISE DOS RESULTADOS E CONCLUSÃO
Neste capítulo serão analisados os resultados dos experimentos e serão apresentadas as
conclusões gerais do projeto.
Para que a análise empírica possa auxiliar este estudo, é interessante também analisar a
complexidade de tempo de cada um dos algoritmos. A Tabela 1 apresenta a complexidade de
tempo dos algoritmos deste trabalho, segundo a implementação adotada. Para o algoritmo
Shell Sort é apresentada apenas a complexidade de pior caso, já que os demais casos, médio e
melhor, podem variar segundo a sequência de incrementos.
Tabela 1 – Complexidade temporal dos algoritmos implementados
Algoritmo Pior caso Caso médio Melhor caso
Bubble Sort
Selection Sort
Insertion Sort
Quick Sort
Heap Sort
Merge Sort
Shell Sort - -
A análise gráfica de todos os algoritmos mostra que à medida que a quantidade de
elementos a serem ordenados cresce, o desempenho decai e o tempo, em milissegundos,
aumenta. Isto significa que o desempenho dos algoritmos só é aproveitável se o conjunto
possuir uma quantidade máxima N de elementos. Em cada gráfico, é possível verificar o valor
aproximado dessa quantidade.
Com a modificação da implementação, o Bubble Sort mostrou-se mais rápido. Na
maior parte das vezes, bem mais rápido que o Selection Sort. Isto não era esperado, já que
popularmente o Bubble Sort é conhecido por ser o pior algoritmo de ordenação em termos de
desempenho. No geral, entre os algoritmos ineficientes, o Insertion Sort foi o que obteve o
melhor desempenho.
Ao contrário dos algoritmos eficientes, cuja taxa de crescimento foi linearmente baixa,
os algoritmos ineficientes mostraram-se inaptos para grandes quantidades de dados. Para
elementos, a taxa de crescimento dessa classe de algoritmos apresentou baixo
desempenho.
O Quick Sort, primeiro algoritmo eficiente avaliado neste projeto, apresentou
resultados satisfatórios. Embora seu pior caso seja , assim como todos os ineficientes, o
17
tempo médio avaliado não ultrapassou o tempo de nenhuma das avaliações dos algoritmos
anteriores. No pior caso esperado deste projeto, ainda ordenou o conjunto com mais de 65
milhões de dados em apenas 20 segundos.
O algoritmo Heap Sort foi o segundo algoritmo eficiente avaliado. Conforme análise
gráfica, este algoritmo alcançou bons resultados em seu desempenho. Embora seja um
algoritmo in-place (que não utiliza memória auxiliar para o processamento) com pior caso
, o Heap Sort não obteve melhores resultados que o Quick Sort.
O algoritmo Merge Sort ainda conseguiu superar o desempenho do Heap Sort. Até
mesmo para os tamanhos maiores, o Merge Sort obteve menor tempo médio entre as
sequências de semente e os tamanhos que o Heap Sort. Junto com o Quick Sort e apenas uma
análise gráfica, a taxa de crescimento do tempo médio do Merge Sort mostrou-se linear em
relação ao tamanho do conjunto.
O algoritmo Shell Sort concluiu a análise de desempenho do projeto. Com três
variações de implementação – tradicional, de Knuth e de Pardons –, o algoritmo Shell Sort foi
um dos melhores algoritmos de ordenação em termos de desempenho. Tanto para pequenas
quantidades quanto para grandes quantidades, a taxa de crescimento desse algoritmo foi
linear.
Para o Shell Sort, é interessante notar que a versão de Pardons obteve um desempenho
significativo em relação a suas demais versões. Embora a versão de Knuth tenha tido um
desempenho melhor que a versão original de Shell, a diferença entre ambas foi pouco
significativa na avaliação dos tamanhos dos conjuntos.
Por fim, a ordenação inversa obteve como melhor resultado entre os ineficientes o
algoritmo Insertion Sort, que rodou em menos da metade do tempo que o Bubble Sort – o
segundo melhor. Entre os algoritmos eficientes, os melhores foram o Quick Sort e a versão
original do algoritmo Shell Sort. Com apenas um milissegundo de diferença, os demais
algoritmos eficientes (Heap Sort, Merge Sort, Shell Sort – Knuth e Shell Sort – Pardons)
ficaram em segundo lugar na avaliação de desempenho geral.
18
REFERÊNCIAS BIBLIOGRÁFICAS
BALAN, A. G. R. Notas de Aula. Universidade Federal do ABC (UFABC), 2012.
CORMEN, T. et al. Algoritmos: teoria e prática. Rio de Janeiro: Elsevier, 2002.
MCCONNELL, J. J. Analysis of Algorithms: an active learning approach. USA: Jones and
Bartlett Publishers, 2008.
SEDGEWICK, Robert. Algorithms in C. USA: Addison-Wesley, 1990.
TOSCANI, L. V.; VELOSO, P. A. S. Complexidade de Algoritmos: análise, projeto e
métodos. Porto Alegre: Editora Sagra Luzzatto, 2002.