algoritmo: do Lat. algorithmos < Ár. alkharizmi: [Inform.] conjunto de etapas bem definidas necessárias para chegar à resolução de um problema.
January 16, 2006
Resolvi resumir nosso estudo sobre algoritmos de ordenação, concluindo esta parte da Série Algoritmos sem explicar o Merge Sort, o Quick Sort e o Bubble Sort (três que eu estava pensando em explicar), mas apenas propondo que algoritmo de ordenação devemos usar para cada caso.
Não vou escrever o custo dos algoritmos de ordenação aqui porque seria uma perda inútil e esse material já pode ser encontrado em vários lugares (na Wikipedia, por exemplo).
Bom... Existem algoritmos de ordenação de vetores bem complexos. Você poderia aprender uma coisa super rápida, mas não vale a pena. Depois de resolver vários problemas que resolvem a ordenação, um professor na UNICAMP (não lembro quem foi) disse:
Não perca tempo com algoritmos de ordenação. Se você programa em C, use qsort; se programa em Pascal ou outra linguagem que não tenha Quicksort implementado, implementa rápido um Insertion Sort.
... e acho que ele estava certo. Talvez para usar em um grande projeto isso não seja o mais indicado, mas para competições não há como fazer melhor. No meio de um algoritmo complexo de fluxo de grafos, por exemplo, eu não tenho tempo para implementar um Quick Sort ou um outro algoritmo de ordenação complexo. Não devemos nos preocupar com isso... Então, usemos algoritmos que tenham implementação fácil, como o Insertion Sort e o Selection Sort. Já que o Insertion Sort tem um custo melhor (
no melhor caso), utilizemos ele.
Isso já discutimos na introdução sobre Ordenação: Basicamente, servem para estudar. O problema de ordenação é um programa muito interessante...
Mas aliás, foi pra não desanimar vocês dizendo que não tem muita utilidade prática que resolvi cortar os algoritmos de ordenação por aqui...
QSORT(3) Linux Programmer's Manual QSORT(3)
NAME
qsort - sorts an array
SYNOPSIS
#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
int(*compar)(const void *, const void *));
Com certeza não é uma das funções mais fáceis de usar... Hehehe... Aliás, confesso que tive alguma dificuldade antes de entender o que isso quer dizer.
Mas é o seguinte... Por o C ser super flexível, você pode criar uma função para comparar dois números. O Qsort exige isto no seu terceiro parâmetro e eu posso fazer o tipo de comparação que eu quiser: ver qual o maior, ver qual o menor ou ver qual tem o fator mínimo maior. Hehehe... É super interessante, mas geralmente não é tão útil, como nesse caso em que só queremos ordenar o vetor de menor pra maior. Então vamos fazer uma função que simplesmente compare dois e diga qual o maior.
O Qsort interpretará o retorno da nossa função da seguinte maneira:
Portanto,
int compara(int *x, int *y) {
if (*x > *y) {
return 1;
}
if (*y > *x) {
return -1;
}
return 0;
}
Bom... Já que não precisamos retornar só 1 e -1, mas sim números maiores que 0 ou menores que 0, poderíamos fazer assim:
int compara(int *x, int *y) {
return *x-*y;
}
Não é verdade?
Não. Essa seria a função mais simples, mas poderia nos causar problemas. Vamos supôr que tentássemos ordenar o
e o
, que são os números que o seu compilador chama de maior e menor inteiro, respectivamente.
Aí ele subtrairia do primeiro o segundo e ia chegar a que número? Ninguém sabe. O programa iria buggar, ele poderia retornar qualquer coisa. Hehehe... Por isso, sempre usemos a de cima e não essa que eu acabei de fazer com a subtração!
E se quiséssemos ordenar de maior pra menor?
int compara(int *x, int *y) {
if (*x < *y) {
return 1;
}
if (*y > *x) {
return -1;
}
return 0;
}
Sentiram a flexibilidade da minha linguagem?
Bom... Agora vamos entender os outros argumentos do qsort() (esses são fáceis).
O primeiro argumento é o tão esperado vetor (na verdade, um ponteiro pra ele ;)). O segundo é o número de elementos no vetor. O terceiro é o tamanho que um elemento ocupa na memória.
#include <stdio.h>
#include <stdlib.h>
int compara(int *x, int *y) {
if (*x > *y) {
return 1;
}
if (*y > *x) {
return -1;
}
return 0;
}
int main() {
int i, vetor[]={53, 22, 55, 34, 1, 126, 72};
qsort(vetor, 7, sizeof(int), (void *)compara);
for (i=0; i<7; i++) {
printf("%d ", vetor[i]);
}
printf("n");
return 0;
}
Ah... Esse cast só serve para o GCC não mostrar um Warning (e passarmos o tipo correto pro QSort), mas acho que não faz diferença nenhuma, quer dizer, ele roda sem problemas sem ele.
Isso já fizemos aqui.
Então comente!
Espero que tenham gostado da conclusão sobre algoritmos de ordenação. Queria reforçar que estou fazendo estes artigos baseando-me principalmente em olimpíadas e por isso sempre estou enfatizando problemas como o tempo de implementação e de execução. Para outras situações, temos vários caminhos diferentes para tomar.
Compare Preços de: notebooks, acer aspire, hp pavilion, computadores, pentium 4, nintendo wii, ps3, celulares, câmeras digitais
Na verdade existem muitos fatores a considerar na hora de escolher um algorítmo de ordenação, particularmente a quantidade de dados a ordenar.
Algorítmos como o Quicksort e Heapsort são um pouco mais complicados que o Insertion Sort, mas podem ter uma vantagem imensa quando a quantidade de dados é grande. O tempo médio do quicksort e do heapsort é proporcional a n*log(n) e do Insertion sort a n*n.
A limitação do Quicksort é que ele é muito lento (cai para n*n) no seu pior caso (que num implementação simplista ocorre quando a entrada já está ordenada!).
Eu gosto muito do Heapsort pelo fato de ser razoavelmente rápido e do tempo variar pouco com os dados sendo ordenados (o tempo máximo é proporcional a n*log(n)). Se, por exemplo, você sabe que no máximo serão ordenados 10.000 registros, basta fazer um teste com um arquivo qualquer com 10.000 registros, se o tempo for bom vai ser bom com qualquer arquivo.
Oi Daniel!
Concordo com você que a quantidade de dados é um dado importante a se analisar. Porém, o principal foco desse artigo foi o uso de um algoritmo de ordenação em uma competição de programação, como a Olimpíada Brasileira de Informática ou a Maratona de Programação…
Não tenho dúvidas de que implementar um Heap Sort seja mais rápido para uma entrada de tamanho razoável (O(n log n) ganha fácil de O(n^2)). Porém, eu acho que numa prova onde temos quatro horas para resolver 4-6 problemas, é bem mais fácil simplesmente rodar um qsort() no C ou implementar um Insertion Sort em Pascal e deixar pra perder tempo com lógicas mais complicadas. Você, que já conhece bem o Heap Sort, conseguiria implementar ele em menos de cinco minutos? Talvez você até consiga, mas sei lá… Pra mim, pelo menos, seria difícil implementar tão rápido. E acho que pra maioria das pessoas que faz essas olimpíadas também. Por isso fiquei por aqui mesmo nos algoritmos de ordenação.
Acho que usar um “qsort()” ali na hora é bem mais fácil.
Ah… Só pra complementar, queria deixar claro que não deixei de explicar o Heap Sort por não achá-lo importante, mas porque eu acho que as pessoas teriam dificuldade para compreendê-lo. Desde o início, eu não planejava incluí-lo na série… Heheh… Ainda mais depois que eu fiz um artigo sobre Análise de Algoritmos e já teve muita gente que se perdeu (e olha que eu omiti várias coisas), imagine então ensinando árvores binárias? A própria estrutura heap é meio complicada pra quem não tá acostumado.
Valeu pelo comentário e mande mais sugestões! ![]()
Tiago,
Xiii, eu não percebi que você estava falando no contexto de competições (e olha que cheguei no seu site dando um Google de OBI2005!).
Realmente, num contexto destes (e na maioria dos casos) é sempre preferível aproveitar as bibliotecas que vem com a linguagem.
[…] O artigo está em outro local agora: Conclusão sobre ordenação […]
gostei muito do seu artigo estou usando para um trabalho no curso de sistemas de informacao
Este design foi copiado do CSS Zen Garden e modificado com autorização de seu autor, Gunta Klavina.
Todo o conteúdo deste site (incluindo textos, imagens, arquivos de áudio e quaisquer outros trabalhos), exceto quando especificado o contrário, está licenciado por Tiago Madeira sob uma Licença Creative Commons.