



Estude fácil! Tem muito documento disponível na Docsity
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Prepare-se para as provas
Estude fácil! Tem muito documento disponível na Docsity
Prepare-se para as provas com trabalhos de outros alunos como você, aqui na Docsity
Os melhores documentos à venda: Trabalhos de alunos formados
Prepare-se com as videoaulas e exercícios resolvidos criados a partir da grade da sua Universidade
Responda perguntas de provas passadas e avalie sua preparação.
Ganhe pontos para baixar
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Comunidade
Peça ajuda à comunidade e tire suas dúvidas relacionadas ao estudo
Descubra as melhores universidades em seu país de acordo com os usuários da Docsity
Guias grátis
Baixe gratuitamente nossos guias de estudo, métodos para diminuir a ansiedade, dicas de TCC preparadas pelos professores da Docsity
Projeto de Algoritmos - Quicksort
Tipologia: Notas de estudo
1 / 6
Esta página não é visível na pré-visualização
Não perca as partes importantes!
O algoritmo do quicksort tem um tempo de execução no pior caso de para um vetor de entrada com elementos. Apesar de ter um tempo de execução relativamente lento, este algoritmo é a melhor escolha prática para ordenação porque seu tempo de execução média é entorno de além do que os fatores constantes escondidos na notação assintótica são bem pequenos. Outra vantagem deste algoritmo é a vantagem de ordenar localmente, o que ajuda sua operação até em memórias virtuais.
Assim como o mergesort , o quicksort baseia-se no paradigma de dividir-e-conquistar. Aqui estão os três passos do processo de dividir-e-conquistar para ordenação de um subconjunto [ ].
Dividir: O arranjo [ ] é particionado em dois subarranjos [ ] e [ ] tais que cada elemento do subarranjo [^ ]^ seja menor ou igual a [ ]^ que, por sua vez, é menor ou igual aos elementos de [ ]. O calculo do índice é parte do procedimento de particionamento.
Conquistar: Os dois subarranjos [ ] e [ ] são ordenados por chamadas recursivas a quicksort.
Combinar: Como os subarranjos são ordenados localmente, não é necessário nenhum trabalho para combiná-los. Todo o arranjo [^ ]^ agora está ordenado.
O procedimento a seguir implementa o quicksort.
1 if 2 then 3 4
Para ordenar um arranjo inteiro, a chamada inicial é [ ].
A chave para o algoritmo é o procedimento , que reorganiza o subarranjo [^ ] localmente.
3 for to 4 do if [ ] 5 then 6 [ ] [ ] 7 [ ] [ ] 8 return
sempre seleciona um elemento [ ] como um elemento pivô ao redor do qual será feito o particionamento do subarranjo [ ]. À medida que o procedimento é executado, o arranjo é particionado em quatro regiões (possivelmente vazias). No início de cada iteração do loop for, nas linhas 3 e 6, cada região satisfaz a certas propriedades que podemos anunciar como um loop invariante: No início de cada iteração do loop das linhas 3 e 6, para qualquer índice do arranjo,
A Figura 4.1 resume esta estrutura. Os índices entre e não são cobertos por quaisquer dos três casos, e os valores nessas entradas não tem nenhum relacionamento particular para o pivô.
Figura 4.1 – As quatro regiões mantidas pelo procedimento em um subarranjo [ ].
É necessário mostrar que esse loop invariante é verdadeiro antes da primeira iteração, que cada do loop iteração mantém o invariante e que o invariante fornece uma propriedade útil para mostrar a correção quando o loop termina.
Inicialização: Antes da primeira iteração do loop, e. Não há nenhuma valor entre e , e nenhum valor entre e ; assim, as duas primeiras condições do loop invariante são satisfeitas de forma trivial. A atribuição na linha 1 satisfaz a terceira condição.
Manutenção: Existem dois casos a se considerar, dependendo do resultado do teste da linha 4. Se [ ]^ , a única ação no loop é incrementar o. Depois que é incrementado, a condição
Figura 4.2 - A operação sobre um exemplo de arranjo. ( a ) O arranjo inicial e as configurações das variáveis. Nenhum dos elementos foi inserido em qualquer uma das duas primeiras partições. ( b ) O valor 2 é “trocado com ele mesmo” e inserido na partição de valores menores. ( c ) - ( d ) Os valores 8 e 7 foram acrescentados à partição de valores maiores. ( e ) Os valores 1 e 8 são permutados, e a partição menor cresce. ( f ) Os valores 3 e 8 são permutados, e a partição menor cresce. ( g ) - ( h ) A partição maior cresce até incluir os elementos 5 e 6 e o loop termina. ( i ) Nas linhas 7 e 8, o elemento pivô é permutado de forma a residir entre as duas partições
Exercícios:
4.2-1. Ilustre a operação de sobre o arranjo { }.
4.2-2. Mostre que o tempo de execução de é.
O desempenho do quicksrort depende do fato de o particionamento ser balanceado ou não. Se o particionamento é balanceado, o algoritmo é exectado assintoticamente tão rápido quanto a ordenação por intercalação. Contudo, se o particionamento é não balanceado, ele pode ser tão lento assintoticamente quanto a ordenação por inserção. Vamos ivestigar informalmente os casos de particionamento balanceado e particionamento não balanceado.
4.3.1 Particionamento no prior caso
O pior caso ocorre quando o algoritmo de particionamento retorna um subproblema com elementos e outro com 0 elementos. Vamos supor que este particionamento não balanceado ocorre a cada chamada recursiva. O particionamento custa. Tendo em vista que a chamada recursiva sobre o arranjo de tamanho 0 tem um custo , a recorrência para o tempo de execução fica
Intuitivamente, se somarmos os custos ocorridos a cada nível de recursão, conseguimos uma série aritmética que tem o valor , que é o valor para o pior caso do método de ordenação por inserção. Este tempo de execução ocorre quando o arranjo de entrada está completamente ordenado, ou seja, quando o particionamento é não balanceado – uma situação que resulta em um tempo de execução igual a para a ordenação por inserção.
4.3.1 Particionamento no melhor caso
Na divisão mais uniforme possível, produz dois subproblemas, cada um de tamanho não maior que ⁄ , pois um tem tamanho ⌊ ⁄ ⌋ e o outro tem tamanho ⌊ ⁄ ⌋. Neste caso, o quicksort é executado com muito mais rapidez. A recorrência pelo tempo de execução é então
que, pelo caso 2 do teorema mestre , tem a solução. Desse modo, o balanceamento equilibrado dos dois lados da partição em cada nível da recursão produz um algoritmo assintoticamente mais rápido.
4.3.2 Particionamento balanceado
O tempo de execução do caso médio do quicksort é muito mais próximo do melhor caso do que do pior. A chave para compreender por que isso poderia ser verdade é entender como o equilíbrio do particionamento se reflete na recorrência que descreve o tempo de execução.
Por exemplo, suponha que o algoritmo de particionamento sempre produza uma divisão proporcional de 9 para 1, que a princípio parece bastante desequilibrada. Então obtemos a recorrência
no tempo de execução do quicksort , onde inserimos explicitamente a constante oculta no termo. A Figura 4.3 mostra a árvore de recursão para esta recorrência. Note que todo nível da árvore tem custo , até ser alcançada um condição limite à profundidade , e então os níveis tem o custo máximo. A recursão termina na profundidade ⁄. O custo total do^ quicksort^ é então^. Portanto, com uma divisão na proporção 9 para 1 em todo nível de recursão, o que intuitivamente parece bastante desequilibrado, o quicksort é executado no tempo – assintoticamente o msesmo tempo de execusão para uma divisão feita exatamente ao meio. A razão é que qualquer divisão de proporcionalidade constante produz uma árvore de recursão de profundidade , onde o custo em cada nívelé. Portanto, o tempo de execução será então sempre que a divisão tiver proporcionalidade constante.
)