Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

Quicksort 4.1. Quicksort O algoritmo do 4.2. Descrição do quicksort, Notas de estudo de Engenharia Informática

Projeto de Algoritmos - Quicksort

Tipologia: Notas de estudo

2014

Compartilhado em 12/06/2014

thiago-souza-cjt
thiago-souza-cjt 🇧🇷

4.5

(34)

75 documentos

1 / 6

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
4. Quicksort
4.1. Quicksort
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.
4.2. Descrição do quicksort
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 é
[ ] .
pf3
pf4
pf5

Pré-visualização parcial do texto

Baixe Quicksort 4.1. Quicksort O algoritmo do 4.2. Descrição do quicksort e outras Notas de estudo em PDF para Engenharia Informática, somente na Docsity!

4. Quicksort

4 .1. Quicksort

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.

4 .2. Descrição do quicksort

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 é [ ].

    1. 1 Particionamento do arranjo

A chave para o algoritmo é o procedimento , que reorganiza o subarranjo [^ ] localmente.

1 [ ]

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,

  1. Se , então [ ].
  2. Se , então [ ].
  3. Se , então [ ].

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

4.3. O desempenho de quicksort

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.

)