Você está na página 1de 19

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

Bubble Sort
60000

Tempo médio (ms) 50000

40000

30000

20000

10000

0
10000 30000 90000
Figura 1 – Gráfico comparativo do tempo médio do algoritmo

Bubble Sort
6000000

5000000
Tempo médio (ms)

4000000

3000000

2000000

1000000

0
270000 810000
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).
8

Selection Sort
70000

60000
Tempo médio (ms)

50000

40000

30000

20000

10000

0
10000 30000 90000
Figura 3 – Gráfico comparativo do tempo médio do algoritmo

Selection Sort
6000000

5000000
Tempo médio (ms)

4000000

3000000

2000000

1000000

0
270000 810000
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.
9

Insertion Sort
16000

Tempo médio (ms) 14000


12000
10000
8000
6000
4000
2000
0
10000 30000 90000
Figura 5 – Gráfico comparativo do tempo médio do algoritmo

Insertion Sort
1600000
1400000
Tempo médio (ms)

1200000
1000000
800000
600000
400000
200000
0
270000 810000
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
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.

Quick Sort
900
800
700
Tempo médio (ms)

600
500
400
300
200
100
0
10000 30000 90000 270000 810000 2430000
Figura 7 – Gráfico comparativo do tempo médio do algoritmo

Quick Sort
25000

20000
Tempo médio (ms)

15000

10000

5000

0
7290000 21870000 65610000
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
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).

Heap Sort
800
700
Tempo médio (ms)

600
500
400
300
200
100
0
10000 30000 90000 270000 810000
Figura 9 – Gráfico comparativo do tempo médio do algoritmo

Heap Sort
120000

100000
Tempo médio (ms)

80000

60000

40000

20000

0
2430000 7290000 21870000 65610000
Figura 10 – Gráfico comparativo do tempo médio do algoritmo
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.

Merge Sort
40000
35000
Tempo médio (ms)

30000
25000
20000
15000
10000
5000
0

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.
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.

Shell Sort
900
800
700
Tempo médio (ms)

600
500
400
300
200
100
0
10000 30000 90000 270000 810000
Figura 12 – Gráfico comparativo do tempo médio do algoritmo

Shell Sort
100000
90000
80000
Tempo médio (ms)

70000
60000
50000
40000
30000
20000
10000
0
2430000 7290000 21870000 65610000
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.


14

Shell Sort - Knuth


100000
90000
Tempo médio (ms)

80000
70000
60000
50000
40000
30000
20000
10000
0

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.

Shell Sort - Pardons


50000
45000
Tempo médio (ms)

40000
35000
30000
25000
20000
15000
10000
5000
0

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.
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.

Ineficientes
20000
18000
16000
14000
Tempo (ms)

12000
10000
8000
6000
4000
2000
0
Bubble Sort Selection Sort Insertion Sort

Figura 16 – Gráfico comparativo da rodada inversa nos algoritmos ineficientes

Eficientes
16,2
16,0
15,8
15,6
Tempo (ms)

15,4
15,2
15,0
14,8
14,6
14,4
Quick Sort Heap Sort Merge Sort Shell Sort Shell Sort - Shell Sort -
Knuth Pardons

Figura 16 – Gráfico comparativo da rodada inversa nos algoritmos 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.

Você também pode gostar