1. Classificação De Dados
Marcelo Araldi1
Fernando Vargas2
Professor Orientador: Antonio Rodrigo Delepiane De Vit3
Com a expansão da internet pelo mundo, a maioria dos estabelecimentos, de todos
os tipos, tem sistemas gerenciais, sistemas de segurança, etc. Conforme o tamanho
do estabelecimento varia também a quantidade de informações que o sistema
precisará tratar e armazenar. Na constante busca pelo melhor desempenho
programadores e analistas de sistemas precisam estudar o local onde o sistema
será implantado para saber adequar o modo como esse sistema trabalhará. A
classificação de dados é muito importante nesse contexto, e esse artigo apresentará
os principais métodos e algoritmos de classificação, desde algoritmos simples para
poucos dados até algoritmos mais complexos para grandes fluxos de dados.
1 Classificação de Dados
Vamos fazer a introdução depois de o trabalho estar pronto.
2 Apresentação dos resultados de classificação
Assim que classificada uma tabela, existem várias maneiras de demonstrar o
resultado dessa classificação. Serão vistos aqui as três principais e formas.
2.1 Contiguidade Física
Neste modo, as entradas são dispostas de forma classificada alterando-se sua
posição física, de forma que no final as mesmas estejam na ordem conforme a
chave de classificação. Abaixo, na figura 1, é demonstrada uma tabela não
ordenada e uma ordenada.
1
IMED, aluno do curso de graduação em Sistemas de Informação, turma SO91N, marcelo.araldi@yahoo.com.br
2
IMED, aluno do curso de graduação em Sistemas de Informação, turma SO91N, fernando.pfv@hotmail.com
3
IMED, mestre em Ciências da Computação, professor do curso de graduação em Sistemas De Informação,
rodrigodevit@imed.edu.br
2. Figura 1: Exemplo de tabela não ordenada(A) e tabela ordenada (B)
fisicamente. Fonte: referencial teórico.
2.2 Vetor Indireto de Ordenação
Diferentemente da Contiguidade Física, quando usamos o método Vetor Indireto de
Ordenação (VIO), as entradas não trocam de posição. Então, a classificação é
apresentada através de um vetor que contém o endereço das entradas em forma
ordenada. Uma grande vantagem deste método é que não é necessário alterar a
posição física dos elementos, o que ainda nos deixa mostrar resultados diferentes
através de vários VIOs. Abaixo segue um exemplo de VIO.
Figura 2: Tabela e seu VIO.
Fonte: referencial teórico.
2.3 Encadeamento
3. Usando o método de Encadeamento para demonstrar o resultado, também não
serão alteradas as posições físicas das entradas. Mas sim, acrescentado mais um
campo para cada posição que indicará a seqüência ordenada, este campo chama-se
próximo e ele aponta para o próximo endereço da ordem de classificação. O
endereço da primeira entrada é separado da tabela, ou pode ainda ser sempre
colocado na primeira posição. Exemplo:
Figura 3: Tabela com campo proximo que mostra ordem classificada.
Fonte: referencial teórico.
Similiar ao VIO, o método de Encadeamento também permite mais de um tipo de
demonstração, sendo possível adicionar mais de um campo próximo, onde será
usado outro critério de classificação, e obter-se-á outra ordem de classificação,
como mostra a imagem abaixo:
Figura 4:Tabela ordenada por dois tipos diferentes de critério.
Fonte: referencial teórico.
4. 3 Ambientes de Classificação
Uma importante e decisiva característica a ser observada acerca da classificação de
dados, é o ambiente no qual estão armazenados estes dados. Os dois tipos de
memórias existentes nos computadores (primária e secundária) são extremamente
influentes no processo de classificação.
A memória primária tem como principal característica o fato de o tempo médio de
acesso a qualquer endereço ser sempre o mesmo, devido ao modo como ela
trabalha. Sendo assim, não importa a seqüência em que os dados são acessados,
pois o tempo para todos será sempre o mesmo.
Fato que já não acontece quando usada a memória secundária (principalmente
discos magnéticos), onde os dados deverão primeiro ser acessados, retirados do
disco e transferidos para a memória primária, pois somente nesta o processador
poderá agir diretamente sobre eles.
Tratando-se de problemas com classificação de dados, pode-se concluir que os
métodos de classificação terão de ser diferentes para ambas as memórias. Na
memória principal a manipulação dos dados pode ser feita em qualquer seqüência,
no entanto, em discos magnéticos, os dados precisam ser manipulados por blocos.
Têm-se, assim, duas denominações para tais processos: classificação interna e
classificação externa.
4 Classificação Interna
Os método de classificação interna são divididos em cinco grupos, diferenciados
pelas técnicas empregadas, são eles:
1. Classificação por Inserção (inserção direta e Shell)
2. Classificação por Troca (bubblesort e quicksort)
3. Classificação por Seleção (seleção direta e heapsort)
4. Classificação por Distribuição (distribuição de chaves e radixsort)
5. Classificação por Intercalação (mergesort)
4.1 Classificação por Inserção
5. Na classificação por inserção faz-se uma separação de dados em segmentos. Um
dos segmentos já é criado ordenado e o outro desordenado. Isso acontece porque o
primeiro segmento só tem um elemento, e assim pode-se afirmar que tal é
ordenado. Então, a partir desse elemento, passaremos os itens do segmento
desordenado para o ordenado, adequando suas posições conforme os valores já
presentes no primeiro segmento, com isso, sabe-se que esse processo desenvolve-
se em n-1 literações.
Existem, ainda, três modos diferentes de classificação por inserção, que apenas são
diferentes devido ao método como realizam a busca pelo lugar certo a inserir o
elemento que vem do segmento desordenado. São eles: inserção direta com busca
seqüencial, inserção direta com busca binária e incrementos decrescentes
(shellsort).
4.1.1 Inserção direta com busca seqüencial
A cada leitura são procurados nodos em desacordo e o faz-se uma troca por vez.
Como mostra a figura abaixo:
Figura 5: Exemplo de funcionamento de inserção direta.
Fonte: referencial teórico.
Desempenho da Inserção Direta com Busca Sequencial
6. O desempenho é influenciado pela ordem inicial das chaves. Temos o pior caso
quando a ordem dos valores está totalmente inversa à ordem correta. Quando isso
ocorre, a quantidade de trocas e comparações é dada por: 1º chave = 1
transposição; 2º chave = 2 transposições; 3º chave = 3 transposiçõe; ... ; (n-1)º
chave = (n-1) transposições.
Já o melhor caso, certamente, é quando a tabela já está organizada, o que torna
desnecessário qualquer troca, a única operação necessária é a de comparação
(n-1).
Pode-se obter o caso médio através da média entre o pior e o melhor caso, ou seja:
Pelo seu resultado lento, a classificação por inserção direta com busca sequencial
deve ser feita somente em tabelas pequenas, que contenham menos de 1000
elementos.
4.1.2 Inserção com busca binária
Mesmo usando o método binário de procura pela posição correta para inserir o
elemento no segmento ordenado, o método de inserção direta com busca binária
apresenta-se pouco vantajoso em relação ao de busca sequencial, pois as
operações de busca e alocação ainda são totalmente proporcionais ao quadrado do
número de chaves.
4.1.3 Método dos Incrementos Decrescentes (Shellsort)
Já que para tabelas com poucos elementos ou tabelas parcialmente ordenadas os
modos de inserção direta funcionam bem, desenvolveu-se um método para grandes
e bagunçadas tabelas. A diferença ocorre na quantidade de segmentos: se nas
maneiras anteriores tínhamos dois segmentos diferentes, agora, no shellsort
teremos vários segmentos e cada elemento será inserido seletivamente em cada um
deles. Um vetor de shellsort pode ser representado da seguinte maneira:
7. Figura 6: Vetor de shellsort. Fonte: referencial teórico.
Onde, primeiramente, em um h inicial, os segmentos formados são classificados por
inserção direta. Após isso, h é diminuído pela metade, o que ocasiona a criação de
novos segmentos, que também serão classificados por inserção direta. Até que h
seja igual 1, os valores vão sendo diminuídos pela metade, quando h for igual a 1,
então o vetor estará ordenado. Um exemplo de vetor por incrementação decrescente
é exibido na figura 7.
Figura 7:Vetor ordenado por Incrementos Decrescentes. Fonte: referencial
teórico.
O desempenho desse método é obtido através de um cálculo muito complexo, mas,
no pior caso, seu resultado será .
4.2 Classificação por Troca
8. Nos métodos de classificação por troca, a característica mais relevante é que ele
compara os elementos dois a dois e realiza as operações necessárias.
4.2.1 Método da Bolha (bubbleSort)
De comportamento simples, o método bolha trabalha de um modo só. Ele compara
todos os pares de elementos consecutivos e enquanto houverem pares não
ordenados, ele continuará varrendo e operando. Assim que uma varredura não
retorna nenhum par desordenado, o vetor está pronto. Na próxima figura um caso de
aplicação de método Bolha em um vetor de cinco elementos:
Figura 8: Funcionamento do método Bolha. Fonte: referencial teórico.
Percebe-se que é possível ao método executar duas trocas em uma mesma
varredura, isso ocorrerá sempre que o vetor estiver parcialmente ordenado. No pior
dos casos, quando a ordem dos elementos estiver totalmente inversa à esperada, o
desempenho do método bolha é ruim, pois desse modo será feita apenas uma troca
por varredura, o que prolonga muito a execução do código. Seu desempenho pode
ser dado por O(n²).No melhor caso, serão necessárias somente n-1 operações, que
verificarão a ordem dos elementos e certificar-se-ão da correta ordenação do vetor.
4.2.2 Método Pente(CombSort)
Um ganho significativo no método bubblesort pode ser obtido usando a estratégia de
promover as chaves em direção às suas posições definitivas por saltos maiores do
que apenas uma casa de cada vez. Esta alternativa consiste em comparar não os
pares consecutivos de chaves, mas pares formados por chaves que distam umas
das outras uma certa distância h.
Na primeira varredura, esta distância h é dada pelo valor h = n div 1,3. Nas
varreduras subseqüentes, esta distância é progressivamente diminuída do fator 1,3
até que seja igual a unidade. Neste momento, o método se confunde com o
bubblesort tradicional.
O fator de 1,3 foi obtido por meio de simulações. A redução do tempo de classificação em
relação ao bubblesort tradicional (sem qualquer tipo de otimização) foi da ordem de 27
vezes.
Esse método de classificação implementa saltos maiores que uma casa por vez.
Usaremos como exemplo o vetor de chaves abaixo: Como no vetor possui 5 chaves,
o salto inicial é igual a 3.
9. OBS.: o salto e dado pelo valor h = n / 1,3 Salto = Int( n / 1,3) = 3 Quando terminar o
procedimento com salto = 1 então é utilizado o algoritmo BOLHA para terminar de
ordenar.
Figura 9: Funcionamento do Combosort. Fonte:http://www.inf.uri.com.br/neilor/apostila-
ED2.pdf
4.2.3 Método da Agitação (Shake Sort)
Esse método nada mais é do que uma aprimoração do método bubble. No caso do
bolha, a cada varredura o algoritmo executa uma troca de chaves, levando apenas a
maior ou a menor para o seu lugar, e com isso todas as outras chaves somente
ganham uma casa de correção. O método shake funciona com o mesmo princípio do
bubble, porém ao final de uma varredura da esquerda para a direita, ele faz outra da
direita para a esquerda (ou vice-versa), o que nos dá maior desempenho no número
de comparações, mas no número de trocas, em relação ao bolha, nada é alterado.
Portanto, o desempenho do shakesort é também representado por O(n²).
4.2.4 Método da Partição e Troca (Quicksort)
A ordenação de Shell aborda o problema de ordenação dividindo a matriz original
em submatrizes, ordenando-as separadamente e dividindo-as de novo para ordenar
as novas submatrizes até que a matriz inteira esteja ordenada. O objetivo é reduzir o
problema original em subproblemas que possam ser resolvidos mais fácil e
rapidamente. O mesmo raciocínio norteou C.A.R. Hoare, que inventou o algoritmo
apropriadamente chamado de quicksort (ordenação rápida).
Apesar de seu desempenho ser logarítmico como muitos outros métodos de
ordenação, o Quicksort é o que apresenta o menor número de operações
elementares por iteração, significando que, mesmo que tenha que efetuar uma
quantidade de iterações proporcional a logn, cada uma delas será muito rápida.
Este método baseia-se na divisão da tabela em duas sub-tabelas, dependendo de
um elemento chamado pivô, normalmente o 1º elemento da tabela. Uma das sub-
10. tabelas contém os elementos menores que o pivô enquanto a outra contém os
maiores. O pivô é colocado entre ambas, ficando na posição correta. As duas sub-
tabelas são ordenadas de forma idêntica, até que se chegue à tabela com um só
elemento.
Esse método de ordenação divide-se em vários passos:
· Escolher para pivô o primeiro elemento da tabela (p=x[1])
· Se os elementos de x forem rearranjados de forma a que o pivô (p) sejam
colocados na posição j e sejam respeitadas as seguintes condições:
1. todos os elementos entre as posições 1 e j-1 são menores ou iguais que o pivô
(p);
2. todos os elementos entre as posições j+1 e n são maiores que o pivô (p)
Então p permanecerá na posição j no final do ordenamento.
· Se este processo for repetido para as sub-tabelas x[1] a x[j-1] e x[j+1] a x[n] e
para todas as sub-tabelas criadas nas iterações seguintes obteremos no final uma
tabela ordenada.
Portanto a parte mais difícil deste método é o procedimento parte que divide a tabela
em 2 sub-tabelas dependendo do pivô.
4.3 Classificação Por Seleção
Os métodos de classificação por seleção caracterizam-se por selecionar um
elemento a cada iteração. Esse elemento é selecionado de acordo com a
necessidade do programa, e após estar seu lugar, não é mais usado pelo programa.
Quando restar somente um elemento que não tenha sido selecionado, esse estará
certamente na posição correta, então a tabela estará organizada. Existem dois
códigos principais de classificação por seleção: SelectSort e o HeapSort.
4.3.1 Seleção Direta (Select Sort)
O método mais simples de seleção direta. O Select sort trabalha fazendo uma busca
seqüencial na qual localiza o menor item da tabela e troca-o de lugar com o o que
estiver na primeira posição da tabela, e assim sucessivamente, ele vai colocando os
elementos em ordem, sempre desconsiderando os já ordenados. Na tabela abaixo
um exemplo de como trabalha o select sort.
11. Figura 10: Tabela ordenada pelo método Select Sort. Fonte: referencial teórico.
Uma vez que o desempenho desse método é O(n²), não recomenda-se a
classificação por seleção direta para tabelas muito grandes.
4.3.2 HeapSort
O termo heap deriva do conceito da estrutua heap, que nada mais é do que uma
árvore binária. Dentre o conceito de heap, têm-se dois tipos: heap máximo (ordem
decrescente) e heap mínimo (ordem crescente). O método de classificação
HeapSort utiliza um heap máximo para ordenar a tabela, e por isso a tabela deve
estar armazenada em uma array. Assim que a tabela encontra-se transformada em
um max heap, pega-se o primeiro elemento do max heap e joga-se o mesmo para a
última posição da tabela.
Quando esse elemento vai para a última posição, estraga-se o max heap, então é
necessário que se faça novamente o processo, chamado de reheap, o max heap é
formado novamente, mas agora desconsiderando o último elemento da tabela que já
encotra-se em sua posição correta. Segue abaixo um esquema de funcionamento
do HeapSort:
12.
13. Figura 11: Funcionamento do HeapSort. Fonte: referencial teórico.
Ao final, sobra um único elemento não ordenado, o que torna desnecessário um
novo max heap. Desse modo, obtém-se a tabela corretamente ordenada.
Analisado o desempenho do HeapSort, concluí-se que o mesmo tem semelhanças
ao QuickSort, já que sua performance pode ser representada por , e esse
desempenho é mantido em qualquer caso, uma vantagem em relação ao QuickSort
que tem seu desempenho diminuído no pior caso.
4.4 Classificação por Intercalação
Os métodos de classificação por Intercalação “quebram” a tabela em duas ou mais
partes, ordenam essas partes separadamente e depois as intercalam, formando uma
só tabela ordenada. O principal algoritmo dessa família é o MergeSort.
4.4.1 Merge Sort (Intercalação Simples)
A maneira de trabalhar do MergeSort é extremamente simples. Ele divide a tabela
em diversos segmentos pequenos, agrupa esses segmentos e intercala-os,
formando assim novos segmentos maiores, ordena-os, intercala-os e forma
novamente segmentos maiores, assim sucessivamente até que seja formado um só
segmento e a tabela esteja totalmente ordenada.
Têm-se, abaixo, um exemplo de classificação por Intercalação Simples:
14. Figura 12:Funcionamento de um MergeSort. Fonte: referencial teórico
Em termos de desempenho, sabe-se que para o pior caso e para o caso médio (que
acontece na maioria das vezes) o método de classificação por Intercalação Simples
tem o mesmo desempenho, que pode ser dado por : . Já o melhor caso
ocorre quando a tabela já está ordenada ou quando ela está totalmente na ordem
inversa à esperada. Para o melhor caso, o desempenho pode ser dado por:
.
Em relação ao QuickSort, o desempenho no MergeSort é o logarítmico mesmo no
pior caso. Já o QuickSort é mais rápido devido à pouca execução de comparações
e sua estrutura mais simples.
15. 4.5 Classificação Por Distribuição de Chaves
Neste tipo de algoritmos temos uma característica diferenciada dos demais. Os
algoritmos de classificação por distribuição de chaves não exercem nenhuma
comparação direta entre as chaves da tabela. Um exemplo muito simples de
entender e muito usado para visualizarmos o trabalho desse algoritmo é ordenar um
baralho.Têm-se um baralho de 52 cartas, totalmente embaralhado. Para ordenar as
cartas, pode-se tomar os seguintes passos:
- Definir 12 montes de cartas, uma para cada valor. (Ás, 1, 2, 3..., Rei)
- Distribuir as cartas nesses montes, assim cada um receberá 4 cartas.
- Redistribuir as cartas em outro monte, agora ordenado conforme a preferência. Ex:
Ás, dois, três..., Rei.
- Definir um monte de cartas para cada naipe.
- Colocar as cartas, ordenadamente, nesses montes, assim será um monte por
naipe, cada um com 12 cartas.
- Após dividir as cartas pelo naipe, refazer um único monte seguindo a ordem
desejada.Ex: Paus, Ouro, Copas, Espada.
- Assim, ao final de todo esse processo, as cartas estarão ordenadas por naipe, e
dentro de cada naipe, haverá uma ordenação por número. Vale lembrar que não
existiram comparações direta entre valor ou naipe de cartas.
Neste método, o principal algoritmo é o RadixSort (Indexação Direta).
4.5.1 RadixSort (Indexação Direta)
O funcionamento desse método segue muito bem o exemplo das cartas. Mas este
usa a comparação dos dígitos ou letras que compõem a chave para ordená-los.
Abaixo as etapas de ordenação do algoritmo RadixSort.
Tendo esta tabela como problema:
Primeiramente, é feita a preparação de dez filas numeras que receberão os
conjuntos de números:
16. Note que a lógica da tabela é a seguinte: os número que terminam em 0, ficam no
espaço da tabela definido para o 0, os que terminam em 1, no espaço do 1, e assim
por diante... até o algarismo 9.
Após terminar a montagem da tabela separada, deve-se retirar os números dela e
passá-los à uma tabela agrupada novamente, seguindo a ordem:
Note que os números estão devidamente ordenados crescentemente pelo último
dígito. Monta-se, novamente, a tabela separada, levando em conta, agora, o
algarismo do meio:
Veja que, igualmente a primeira tabela, esta tem os números devidamente
separados, cada qual no seu espaço.
Novamente, a tabela agrupada é montada, seguindo a ordem dos números:
E, finalmente, monta-se, novamente, a tabela separada, que agora levará em conta
o primeiro dígito de cada número para separá-los:
17. Após a montagem da última tabela separada, têm-se os números em ordem perfeita.
Assim, remontando a tabela agrupada, os números ficarão crescentemente
ordenados:
O desempenho deste algoritmo não é muito variável, já que o mesmo não efetua
comparações diretas entre as chaves, então não é atrapalhado pelo número elevado
ou reduzido de elementos em uma tabela. Seu único declínio dá-se pela grande
quantidade de divisões que o mesmo efetuará até chegar a ordem correta. Afirma-se
que se desempenho é demonstrado por O(n).