Particionamento Quicksort: Hoare vs. Lomuto


83

Existem dois métodos de partição quicksort mencionados em Cormen:

Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
    repeat
        j = j - 1
    until A[j] <= x
    repeat
        i = i + 1
    until A[i] >= x
    if i < j
        swap( A[i], A[j] )
    else
        return j

e:

Lomuto-Partition(A, p, r)
x = A[r]
i = p - 1
for j = p to r - 1
    if A[j] <= x
        i = i + 1
        swap( A[i], A[j] )
swap( A[i +1], A[r] )
return i + 1

Desconsiderando o método de escolha do pivô, em que situações uma é preferível à outra? Eu sei, por exemplo, que o Lomuto pré-forma relativamente mal quando há uma alta porcentagem de valores duplicados (ou seja, onde digamos que mais de 2/3 da matriz é o mesmo valor), onde Hoare se sai bem nessa situação.

Que outros casos especiais tornam um método de partição significativamente melhor que o outro?


2
Não consigo pensar em nenhuma situação em que Lomuto seja melhor que Hoare. Parece que o Lomuto realiza trocas extras sempre A[i+1] <= x. Em uma matriz classificada (e tendo em vista os pivôs razoavelmente escolhidos), Hoare quase não troca e Lomuto faz uma tonelada (uma vez que j fica pequeno o suficiente, então todos A[j] <= x). O que estou perdendo?
Wandering Logic

2
@WanderingLogic Não tenho certeza, mas parece que a decisão de Cormen de usar a partição Lomuto em seu livro pode ser pedagógica - parece ter um loop invariante bastante direto.
22413 Robert S. Barnes

2
Observe que esses dois algoritmos não fazem a mesma coisa. No final do algoritmo de Hoare, o pivô não está em seu lugar final. Você pode adicionar um swap(A[p], A[j])no final do Hoare's para obter o mesmo comportamento para ambos.
Aurélien Ooms

Você também deve verificar i < jnos 2 loops de repetição do particionamento do Hoare.
Aurélien Ooms

@ AurélienOoms O código é copiado diretamente do livro.
Robert S. Barnes

Respostas:


92

Dimensão Pedagógica

Devido à sua simplicidade, o método de particionamento do Lomuto pode ser mais fácil de implementar. Há uma boa anedota em Programming Pearl, de Jon Bentley, sobre Classificação:

“A maioria das discussões sobre o Quicksort usa um esquema de particionamento baseado em dois índices que se aproximam [...] [isto é, de Hoare]. Embora a idéia básica desse esquema seja direta, sempre achei os detalhes complicados - uma vez passei a maior parte de dois dias perseguindo um bug escondido em um curto loop de particionamento. Um leitor de um rascunho preliminar reclamou que o método padrão de dois índices é de fato mais simples que o de Lomuto e esboçou algum código para expressar sua opinião; Parei de cuidar e encontrei dois insetos.

Dimensão de desempenho

Para uso prático, a facilidade de implementação pode ser sacrificada em prol da eficiência. Em uma base teórica, podemos determinar o número de comparações e swaps de elementos para comparar o desempenho. Além disso, o tempo de execução real será influenciado por outros fatores, como desempenho do cache e previsões incorretas de ramificações.

Como mostrado abaixo, os algoritmos se comportam de maneira muito semelhante em permutações aleatórias, exceto pelo número de swaps . Lá, Lomuto precisa de três vezes mais que Hoare!

Número de comparações

n1n

Número de Swaps

O número de trocas é aleatório para os dois algoritmos, dependendo dos elementos na matriz. Se assumirmos permutações aleatórias , ou seja, todos os elementos são distintos e toda permutação dos elementos é igualmente provável, podemos analisar o número esperado de swaps.

1,,n

Método de Lomuto

jA[j]x1,,nx1xx1x

{1,,n}1n

1nx=1n(x1)=n212.

n

Método de Hoare

x

ijxij

x

Hyp(n1,nx,x1)nxx1(nx)(x1)/(n1)x

Por fim, calculamos a média novamente de todos os valores de pivô para obter o número geral esperado de swaps para o particionamento de Hoare:

1nx=1n(nx)(x1)n1=n613.

(Uma descrição mais detalhada pode ser encontrada na minha tese de mestrado , página 29.)

Padrão de acesso à memória

Ambos os algoritmos usam dois ponteiros na matriz que a varrem sequencialmente . Portanto, ambos se comportam em cache wrt quase ideal.

Elementos iguais e listas já ordenadas

Como já mencionado pela Wandering Logic, o desempenho dos algoritmos difere drasticamente para listas que não são permutações aleatórias.

n/2

0ijO(nlogn)

0A[j] <= xi=nΘ(n2)

Conclusão

O método do Lomuto é simples e fácil de implementar, mas não deve ser usado para implementar um método de classificação de bibliotecas.


16
Uau, essa é uma resposta detalhada. Bem feito!
Raphael

Tem que concordar com Raphael, resposta muito legal!
Robert S. Barnes

1
Gostaria de fazer um pequeno esclarecimento de que, à medida que a proporção de elementos únicos e elementos totais diminui, o número de comparações que o Lomuto faz aumenta significativamente mais rápido do que as do Hoare. Provavelmente, isso se deve ao particionamento deficiente da parte de Lomuto e ao bom particionamento médio da parte de Hoare.
Robert S. Barnes

Ótima explicação dos dois métodos! Obrigado!
v Kouk

Você pode criar facilmente uma variante do método Lomuto que pode extrair todos os elementos iguais ao pivô e deixá-los fora da recursão, embora não tenha certeza se isso ajudaria ou dificultaria o caso médio.
Jakub Narębski 19/10

5

Alguns comentários foram adicionados à excelente resposta de Sebastian.

Vou falar sobre o algoritmo de rearranjos de partição em geral e não sobre seu uso específico para o Quicksort .

Estabilidade

O algoritmo de Lomuto é semi - estável : a ordem relativa dos elementos que não satisfazem o predicado é preservada. O algoritmo de Hoare é instável.

Padrão de acesso ao elemento

O algoritmo do Lomuto pode ser usado com lista vinculada única ou estruturas de dados somente para frente semelhantes. O algoritmo de Hoare precisa de bidirecionalidade .

Número de comparações

n1n

Mas, para fazer isso, temos que sacrificar 2 propriedades:

  1. A sequência a ser particionada não deve estar vazia.
  2. O algoritmo não pode retornar o ponto de partição.

n

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.