Você está na página 1de 27

QuickSort

Algoritmos e Estuturas de Dados

Ctia Vaz

QuickSort
Algoritmo do tipo dividir para conquistar Ideia do algoritmo: efectuar partio dos dados e ordenar as vrias partes independentemente (de forma recursiva) posicionamento da partio a efectuar depende dos dados de entrada processo de partio crtico uma vez efectuada a partio, cada uma das partes pode por sua vez ser ordenada pelo mesmo algoritmo .
Ctia Vaz
2

QuickSort-partio
public static void quicksort(int a[], int left, int right){ int i; if (right <= left) return; i = partition(a, left, right); quicksort(a, left, i-1); quicksort(a, i+1, right); }

Processo de partio rearranja os dados de forma a que as trs condies seguintes sejam vlidas (de a[left] a a[right]):
o elemento a[i], para algum i, fica, aps a partio, na sua posio final; nenhum dos elementos em a[left] a[i-1] maior do que a[i]; nenhum dos elementos em a[i+1] a[right] menor do que a[i].

Ctia Vaz

QuickSort-partio
Para cada processo de partio, pelo menos um elemento fica na sua posio final. Aps partio, o array fica sub-dividido em duas partes que podem ser ordenadas separadamente A ordenao completa conseguida atravs de partio + aplicao recursiva do algoritmo aos dois subconjuntos de dados da resultantes.

Ctia Vaz

QuickSort-partio
Estratgia para a partio escolher a[right] arbitrariamente para ser o elemento de partio percorrer o array a partir da esquerda at encontrar um elemento maior que ou igual ao elemento de partio (a[right]) percorrer o array a partir da direita at encontrar um elemento menor que ou igual ao elemento de partio (a[right])
trocamos as posies destes dois elementos!

procedimento continua at nenhum elemento esquerda de a[right] ser maior que ele, e nenhum elemento direita de a[right] ser menor que ele
completa-se trocando a[right] com o elemento mais esquerda do sub-array da direita Ctia Vaz

QuickSort-partio
public static int partition(int a[], int left, int right){ int i, j; //i da esquerda para a direita, j ao contrrio int v; //elemento de partio v = a[right]; i = left-1; j = right; for (;;) { while (less(a[++i], v) && i<right) ; while (less(v, a[--j]) && j>left); if (j == left) break; if (i >= j) break; exch(a,i, j); } exch(a,i,right); return i; }

Ctia Vaz

QuickSort-partio
public static void partition(int[] a, int l, int r){ int x=a[r]; int i=l-1; for(int j=l; j<r;j++){ if(a[j]<=x){ i++; exch(a,i,j); } } i++; exch(a,r,i); return i; }

Ctia Vaz

QuickSort-partio
Processo de partio no estvel
qualquer chave pode ser movida para trs de vrias outras chaves iguais a si (que ainda no foram examinadas)

A eficincia do processo de ordenao depende de como a partio divide os dados (do elemento de partio)
ser tanto mais equilibrada quanto mais perto este elemento estiver do meio do array na sua posio final

Ctia Vaz

QuickSort-caractersticas
Pode ser muito ineficiente em casos patolgicos!
Propriedade: quicksort usa cerca de N2/2 comparaes no pior caso.
Demonstrao: se o array j estiver ordenado, todas as parties degeneram e o programa chama-se a si prprio N vezes; o nmero de comparaes de N + (N-1) + (N-2) + + 2 + 1 = (N + 1) N / 2 (mesma situao se o ficheiro estiver ordenado por ordem inversa) Nota: No apenas o tempo necessrio para a execuo do algoritmo cresce quadraticamente como o espao necessrio para o processo recursivo de cerca de N o que inaceitvel para ficheiros grandes.

Ctia Vaz

QuickSort-caractersticas
Melhor caso: quando cada partio divide o ficheiro de entrada exactamente em metade
nmero de comparaes usadas por quicksort satisfaz a recurso de dividir para conquistar C(N)= 2 C( N /2) + N

e logo

C(N) =O( N log N)

Ctia Vaz

10

QuickSort-caractersticas
Propriedade: quicksort usa cerca de 2N lg N comparaes em mdia Demonstrao: A frmula de recorrncia exacta para o nmero de comparaes utilizado por quicksort para ordenar N nmeros distintos aleatoriamente posicionados

o termo N+1 cobre o custo de comparar o elemento de partio com os restantes (2 comparaes extra: i e j cruzamse) cada elemento tem probabilidade 1/N de ser o elemento de partio aps o que ficamos com duas sub-arrays de tamanhos k-1 e N-k
Ctia Vaz
11

QuickSort-caractersticas
Esta anlise assume que os dados esto aleatoriamente ordenados e tm chaves diferentes
pode ser lento em situaes em que as chaves no so distintas ou que os dados no esto aleatoriamente ordenados

O algoritmo pode ser melhorado para reduzir a probabilidade que um caso mau ocorra.

Ctia Vaz

12

Quick Sort- anlise caso mdio

Multiplicando por N:

Subtraindo a mesma frmula para N-1:

Ctia Vaz

13

Quick Sort- anlise caso


mdio
Dividindo ambos os lados por N(N+1):

Usando a recorrncia:

como:

Quick sort usa aproximadamente 2 N lg N comparaes em mdia Ctia Vaz

14

QuickSort-caractersticas
Questes mais relevantes:
possvel reduo de desempenho devido ao uso de recurso tempo de execuo dependente dos dados de entrada tempo de execuo quadrtico no pior caso espao/memria necessrio no pior caso linear
um problema srio (para uma estrutura de dados de grandes dimenses)

Problema do espao est associado ao uso de recurso:


recurso implica chamada a funo e logo a carregar dados na pilha/stack do computador
Ctia Vaz

15

QuickSort- verso no
recursiva
Usamos uma pilha (stack) explcita
pilha contm sub arrays a serem processados (sub arrays a ordenar) quando precisamos de um sub-array para processar tiramola da pilha (i.e.fazemos um pop() do stack) por cada partio criamos duas sub-arrays e colocamos ambas na pilha (i.e. fazemos dois push() para o stack) substitui a pilha do computador que usado na implementao recursiva

Conduz a uma verso no recursiva de quicksort()

Ctia Vaz

16

QuickSort
verso no recursiva de quicksort()
verifica os tamanhos dos dois sub-arrays e coloca o maior deles primeiro na pilha (e o menor depois; logo o menor retirado e tratado primeiro) ordem de processamento dos sub-arrays no afecta a correcta operao da funo ou o tempo de processamento mas afecta o tamanho da pilha

Ctia Vaz

17

QuickSort-verso no recursiva
static void quicksort(int a[], int left, int right){ Stack<Integer> s = new Stack<Integer>(); s.push(left);s.push(right); while (!s.empty()) { right = s.pop(); left = s.pop(); if (right <= left) continue; int i = partition(a, left, right); if (i-left > right-i){ s.push(left); s.push(i-1);} s.push(i+1);s.push(right); if (i-left <= right-i){ s.push(left);s.push(i-1);} } }

Ctia Vaz

18

QuickSort-verso no recursiva
A estratgia de colocar o maior dos sub-arrays primeiro na pilha
garante que cada entrada na pilha no maior do que metade da que estiver antes dela na pilha pilha apenas ocupa lg N no pior caso
que ocorre agora quando a partio for sempre no meio da tabela em ficheiros aleatrios o tamanho mximo da pilha bastante menor

Estratgia que pode ser tambm na verso recursiva.

Propriedade: se a menor dos dois sub-arrays for ordenado primeiro, a pilha nunca necessita mais do que lg N entradas quando quicksort usado para ordenar N elementos.
Demonstrao: no pior caso o tamanho da pilha inferior a T(N) em que T(N) satisfaz a recorrncia T(N) = T (|N/2|) + 1 (T(0) = T(1) = 0)
Ctia Vaz
19

QuickSort-verso no
recursiva
Propriedade: se a menor dos dois sub-arrays for ordenado primeiro, a pilha nunca necessita mais do que lg N entradas quando quicksort usado para ordenar N elementos. Demonstrao: no pior caso o tamanho da pilha inferior a T(N) em que T(N) satisfaz a recorrncia T(N) = T (|N/2|) + 1 (T(0) = T(1) = 0)

Ctia Vaz

20

QuickSort- melhoramentos
Algoritmo pode ainda ser melhorado:
porqu colocar ambas os sub-arrays na pilha se um deles de imediato retirado
teste de right <= left feito assim que os sub-arrays saem da pilha
seria melhor nunca as l ter colocado!

ordenao de sub-arrays de pequenas dimenses pode ser efectuada de forma mais eficiente
ao mudar o teste no incio da funo recursiva para uma chamada if (right-left <= M) insertionSort(a, left, right)

em que M um parmetro a definir na implementao


Algoritmo hbrido, para arrays pequenos usar o insertion sort.

Ctia Vaz

21

QuickSort- melhoramentos
Utilizar um elemento de partio que com alta probabilidade divida o array pela metade
pode-se usar um elemento aleatoriamente escolhido
evita o pior caso (i.e. pior caso tem baixa probabilidade de acontecer) um exemplo de um algoritmo probabilstico

pode-se escolher alguns (ex: trs) elementos do array e usar a mediana dos trs como elemento de partio
escolhendo os trs elementos da esquerda, meio e direita da tabela podemos incorporar sentinelas na ordenao ordenamos os trs elementos, depois trocamos o do meio com a[right-1] e corremos o algoritmo de partio em a[left+1] a[right-2] este melhoramento chama-se o mtodo da mediana de trs. Ctia Vaz

22

QuickSort- melhoramentos
Mtodo da mediana de trs melhora quicksort por trs razes
o pior caso mais improvvel de acontecer na prtica
dois dos trs elementos teriam de ser dos maiores ou menores do array e isto teria de acontecer constantemente a todos os nveis de partio

reduz o tempo mdio de execuo do algoritmo embora apenas por cerca de 5% caso particular de mtodos em que se faz amostragem dos dados para estimar as suas propriedades

Conjuntamente com o mtodo de tratar de pequenos arrays pode dar ganhos de 20 a 25%
Ctia Vaz

23

QuickSort- estudo emprico

Quicksort cerca de 2 vezes mais rpido que shellsort para arrays grandes aleatoriamente ordenados. Usando insertion para pequenos arrays e a estratgia de
mediana-de-trs melhoram cada um a eficincia por um factor de 10%
Ctia Vaz
24

QuickSort- chaves duplicadas


Arrays com um grande nmero de chaves duplicadas
so frequentes na prtica
desempenho de quicksort pode ser substancialmente melhorado se
todas as chaves forem iguais
quicksort mesmo assim faz N lg N comparaes

houver duas chaves distintas


reduz-se ao problema anterior para cada sub-array

natureza recursiva de quicksort garante que haver frequentemente sub-arrays de items com poucas chaves
uma possibilidade dividir o array em trs partes
cada uma para chaves menores, iguais e maiores que o elemento de partio

Ctia Vaz

25

QuickSort- chaves duplicadas


Soluo: fazer uma partio em trs partes
manter chaves iguais ao elemento de partio que so encontradas no sub-array da esquerda do lado esquerdo do array manter chaves iguais ao elemento de partio que so encontradas no sub-array da direita do lado direito do array

left

right

Quando os ndices de pesquisa se cruzam sabemos onde esto os elementos iguais ao de partio e fcil coloc-los em posio
trabalho extra para chaves duplicadas proporcional ao nmero de chaves duplicadas: funciona bem se no houver chaves duplicadas linear quando h um nmero constante de chaves Ctia Vaz

26

QuickSort- partio em trs


static void quicksort(int a[], int left, int right){ int i, j, k, p, q, v; if (right <= left) return; v = a[right]; i = left-1; j = right; p = left-1; q = right; for (;;) { while (less(a[++i], v) && i<right) ; while (less(v, a[--j]) && j>left); if (i >= j) break; exch(a,i,j); if (equal(a[i],v)) { p++; exch(a,p,i);} if (equal(v,a[j])) { q--; exch(a,q,j);} } exch(a,i,right); j = i-1; i = i+1; for (k = left ; k < p; k++, j--) exch(a,k,j); for (k = right-1; k > q; k--, i++) exch(a,k,i); quicksort(a, left, j); quicksort(a, i, right); }

Ctia Vaz

27

Você também pode gostar