Baixe o app para aproveitar ainda mais
Prévia do material em texto
08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 1/26 ANÁLISE DEANÁLISE DE ALGORITMOS ALGORITMOS UNIDADE 3 –UNIDADE 3 – RECORRÊNCIAS ERECORRÊNCIAS E COMPLEXIDADE DECOMPLEXIDADE DE ALGORITMOS DEALGORITMOS DE ORDENAÇÃO ORDENAÇÃO Autor: Thiago Nascimento Rodrigues Autor: Thiago Nascimento Rodrigues Revisor: Bruno Carreira Coutinho Silva Revisor: Bruno Carreira Coutinho Silva INICIAR Introdução Caro(a) estudante, Diversos problemas computacionais são modelados por relações ou funções cuja definição é dada por meio de regras baseadas na própria relação ou função. Essa é a essência do conceito de recorrência, o qual é amplamente empregado em problemas conhecidos como recursivos. Entender como manipular essa classe de problemas e conhecer técnicas para descrever o comportamento assintótico dos algoritmos relacionados é de grande importância para o desenvolvimento de soluções eficientes e precisas. Nesta unidade, vamos conhecer um importante resultado que dá suporte à obtenção de 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 2/26 3.1 Recorrências: método do teorema mestre Dividir e conquistar é provavelmente a técnica geral de projeto de algoritmos mais conhecida. De fato, alguns dos algoritmos mais eficientes para certos tipos de problemas são implementações específicas dessa estratégia geral. Algoritmos de divisão e conquista são construídos em conformidade com três passos gerais: Um problema é dividido em vários subproblemas do mesmo tipo e idealmente do mesmo tamanho aproximado. Os subproblemas são resolvidos, normalmente de forma recursiva, embora às vezes um algoritmo diferente seja empregado, especialmente quando os subproblemas se tornam pequenos o suficiente. Se necessário, as soluções para os subproblemas são combinadas para se obter uma solução do problema original. O caso mais típico da técnica de divisão e conquista é apresentado esquematicamente no Diagrama 1, no qual um problema é dividido em dois subproblemas menores. Como exemplo prático, o problema de calcular a soma dos números pode ser considerado. Se n > 1 [ #PraCegoVer : n maior que 1], o problema pode ser dividido em dois: calcular a soma dos primeiros ⌊n/2⌋ [ #PraCegoVer : piso de n sobre 2] números e calcular a soma dos números ⌈n/2⌉ [ #PraCegoVer : teto de n sobre 2] restantes. Naturalmente, se n = 1 [ #PraCegoVer : n igual a 1], basta simplesmente retornar a [ #PraCegoVer : a índice 0] como resposta. Assim que cada uma das duas soluções fechadas para determinados tipos de recorrências de maneira direta. Além disso, os conceitos de recursão serão analisados sob a perspectiva de algoritmos que lidam com o problema de ordenação de dados em memória primária. Uma técnica conhecida como dividir e conquistar será ainda explorada no contexto de dois algoritmos projetados para essa classe de problema. Bons estudos! 0 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 3/26 somas for calculada aplicando o mesmo método recursivamente, os seus valores podem ser somados para obter o resultado em questão (LEVITIN, 2012): Diagrama 1 – Caso típico de divisão e conquista Fonte: LEVITIN, 2012, p. 170. (Adaptado). Conforme apresentado, no caso mais típico de divisão e conquista, a instância de um problema de tamanho n é dividida em duas instâncias de tamanho n/2 [ #PraCegoVer : n 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 4/26 sobre 2]. De forma geral, uma instância de tamanho n pode ser dividida em b instâncias de tamanho n/b [ #PraCegoVer : n sobre b], com a delas precisando ser resolvidas – a e b são constantes em que a ≥ 1 e b > 1 [ #PraCegoVer : a maior ou igual a 1 e b maior que 1]. Para simplificação da análise, é possível supor que o tamanho n é uma potência de b. Neste caso, a seguinte recorrência para o tempo de execução T(n) [ #PraCegoVer : T de n] é obtida: em que f(n) é uma função que contabiliza o tempo gasto na divisão de uma instância de tamanho n em instâncias de tamanho n/b e combinando suas soluções. Considerando essa recorrência no formato padrão de recorrências, temos que T(n) é, no máximo, uma constante para todo n suficientemente pequeno. Para valores grandes de n , o caso geral seria dado por Em que: a corresponde ao número de chamadas recursivas; b indica o fator de redução do tamanho da entrada; d é o expoente do tempo de execução relativo aos passos de combinação. O caso básico de uma recorrência no formato padrão afirma que quando o tamanho da entrada é suficiente, nenhuma chamada recursiva é necessária; logo, o problema pode ser resolvido em tempo O (1). Por outro lado, o caso geral assume que o algoritmo faz a chamadas recursivas, cada uma aplicada a um subproblema menor do que a entrada original e cuja redução se dá por um b. Além disso, o caso geral realiza O(n ) [ #PraCegoVer : O de n elevado a d] operações funciona fora das chamadas recursivas (ROUGHGARDEN, 2017). Uma técnica muito empregada para se encontrar uma solução fechada para recorrências no formato padrão é conhecido como teorema mestre. De acordo com Cormen et al. (2009), o teorema mestre oferece um passo a passo para resolver recorrências da forma T(n) = aT(n/b) + f(n) [ #PraCegoVer : T de n é igual ao produto de a por T de n sobre b somado a f de n], em que a ≥ 1 e b > 1 [ #PraCegoVer : a maior ou igual a 1 e b maior d 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 5/26 que 1] são constantes e f(n) é uma função assintoticamente positiva. Uma vez satisfeitas essas condições, a solução de recorrências desse tipo pode ser dada pela avaliação dos seguintes casos: (Caso 1) Se para alguma constante ε > 0 [ #PraCegoVer : épsilon maior que 0], então (Caso 2) Se então (Caso 3) Se e se af(n/b) ≤ cf(n) [ #PraCegoVer : a vezes f de n sobre b é menor ou igual a c vezes f de n] para alguma constante c > 1 e n for suficientemente grande, então T(n) = Θ(f(n)) [ #PraCegoVer : T de n é igual a theta de f de n]. Clique nos cards para saber mais sobre o Teorema Mestre. Teorema Mestre Se f(n) ∈ Θ(n ) [ #PraCegoVer : f de n pertence a theta de n elevado a d] onde d ≥ 0 na recorrência T(n) = aT(n/b) + f(n) [ #PraCegoVer : T de n igual ao produto de a com T de n sobre b e isso somado a f de n] então » Clique nas abas para saber mais sobre o assunto d 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 6/26 Importante observar que o resultado da recorrência depende da comparação entre Para que o teorema possa ser aplicado, é preciso que a diferença entre essas duas funções seja polinomial, isto é, tem que ser pelo menos n [ #PraCegoVer : n elevado a épsilon] . Em função disso, o teorema não se aplica a todas as recorrências. De maneira intuitiva, a maior das funções determina a solução para a recorrência. No caso 1, se a função é maior, então a solução é No caso 3, se a função f(n) [ #PraCegoVer : f de n] é maior, então a solução é T(n) = Θ((f)n) [ #PraCegoVer : T de n é igual a theta de f de n]. No caso 2, se as duas funções têm o mesmo tamanho, é preciso multiplicar um fator logarítmico e a solução é VOCÊ SABIA? Os três casos abrangidos pelo teorema mestre não cobrem todas as possibilidades para f(n) . Há uma lacuna entre os casos 1 e 2 quando f(n) é menor que mas não polinomialmente menor. Da mesma forma, existe uma lacuna entre os casos 2 e 3 quando f(n) é maior queε 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 7/26 mas não polinomialmente maior. Se a função f(n) cair em uma dessas lacunas, ou se a condição de regularidade no caso 3 falhar, não será possível usar o método mestre para resolver a recorrência (SKIENKA, 2008). Para utilizar o teorema mestre, basta determinar em qual caso (se aplicável) a recorrência a ser resolvida se encaixa. Por exemplo, a seguinte função: T(n) = 9T(n/3) + n [ #PraCegoVer : T de n é igual a 9 vezes T de n sobre 3 e isso somado a n]. Para essa recorrência, os termos considerados pelo teorema têm os seguintes valores: a = 9; [ #PraCegoVer : a igual a 9] b = 3; [ #PraCegoVer : b igual a 3] f(n) = n . [ #PraCegoVer : f de n igual a n] Logo, Como onde ε = 1 [ #PraCegoVer : épsilon é igual a 1], o teorema pode ser aplicado para o caso 1, o que leva à solução da recorrência como T(n) = Θ(n ) [ #PraCegoVer : T de n é igual a theta de n ao quadrado]. Um segundo exemplo de aplicação do teorema pode ser analisado com a função T(n) = T(2n/3) + 1 [ #PraCegoVer : T de n é igual a T de 2n sobre 3 e isso somado a 1]. Neste caso, os termos envolvidos são: a = 1; [ #PraCegoVer : a igual a 1] b = 2 /3; [ #PraCegoVer : b igual a dois terços] f(n) = 1. [ #PraCegoVer : f de n igual a 1] Logo, 2 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 8/26 Como o caso 2 do teorema pode ser aplicado e o resultado é dado por T(n) = Θ(log(n)) [ #PraCegoVer : T de n é igual a theta de log de n]. Como terceiro exemplo, o problema de encontrar a solução para a recorrência T(n) = 3T(n/4) + nlog(n) [ #PraCegoVer : T de n é igual a 3 vezes T de n sobre 4 e esse produto é somado a n vezes log de n]. Neste caso, tem-se: a = 3, [ #PraCegoVer : a igual a 3] b = 4, [ #PraCegoVer : b igual a 4] f(n) = nlog(n) . [ #PraCegoVer : f de n igual ao produto de n com log de n] Logo, Como onde ε ≈ 0,2 [ #PraCegoVer : épsilon é aproximadamente 0,2], o caso 3 do teorema pode ser aplicado se a condição de regularidade de f(n) for provada ser satisfeita. Para um número n suficientemente grande, tem-se que: Consequentemente, o caso 3 pode ser empregado e a solução da recorrência é dada por T(n) = Θ(nlog(n)) [ #PraCegoVer : T de n é igual a theta de n log de n]. Teste seus conhecimentos Atividade não pontuada. 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 9/26 VOCÊ SABIA? Merge sort é um algoritmo relativamente antigo e que era conhecido por John von Neumann já em 1945. Apesar de ter mais de 70 anos, esse algoritmo ainda é um dos métodos escolhidos para executar operações de ordenação. Na prática, Merge sort é o algoritmo de ordenação padrão em uma série de bibliotecas de programação (ROUGHGARDEN, 2017). 3.2 Complexidade do algoritmo de decisão e conquista Merge sort Muitos dos algoritmos conhecidos são recursivos em estrutura, isto é, para resolver um determinado problema eles chamam recursivamente a si próprios uma ou mais vezes e lidam com subproblemas relacionados. Esses algoritmos normalmente seguem uma abordagem de dividir e conquistar: eles dividem o problema em vários subproblemas que são semelhantes ao problema original, mas menores em tamanho, resolvem os subproblemas recursivamente e, em seguida, as soluções são combinadas para construir uma solução para o problema original. O algoritmo de ordenação Merge sort segue de perto o paradigma dividir e conquistar. Intuitivamente, ele funciona da seguinte maneira: Dividir: uma sequência de n elementos é dividida em duas subsequências de n /2 elementos cada a fim de que ambas sejam ordenadas; Conquistar: as duas subsequências são ordenadas recursivamente usando o Merge sort; Combinar : as duas subsequências ordenadas são mescladas para produzir a resposta ordenada. A recursão é interrompida ou atinge o seu limite quando a sequência a ser ordenada alcança o comprimento 1, caso este em que não há trabalho a ser feito, uma vez que toda sequência de comprimento 1 já está ordenada. 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 10/26 Uma maneira de visualizar a execução do algoritmo Merge sort é por meio de uma árvore binária T, chamada de árvore Merge sort. Cada nó de T representa uma invocação recursiva (ou chamada) do algoritmo sobre uma sequência S a ser ordenada. Os filhos de um nó v de T estão associados às chamadas recursivas que processam as subsequências S e S de S. Os nós externos de T estão associados a elementos individuais de S, correspondendo a instâncias do algoritmo que não fazem chamadas recursivas (GOODRICH, TAMASSIA e GOLWASSWER, 2014). O Diagrama 2 apresenta um exemplo de árvore binária de execução do algoritmo Merge sort. Diagrama 2 – Árvore binária de execução do Merge sort Fonte: GOODRICH, TAMASSIA e GOLWASSWER, 2014, p. 533. O pseudocódigo do algoritmo Merge sort é descrito a seguir. Nesse algoritmo, a ordenação de um dado vetor A de n posições é feita com sua divisão em duas metades, ordenando cada uma delas recursivamente e, então, mesclando os dois vetores menores ordenados em um só. Quando o tamanho dos subvetores é menor ou igual a 1, ou seja, tem no máximo um elemento, ele já se encontra ordenado. A operação principal do algoritmo (linha 6) é a mesclagem de dois subvetores ordenados na etapa de “combinação” da estratégia de divisão e conquista. O procedimento Mesclar realiza junção dos subvetores dois a dois para formar um único vetor ordenado. 1 2 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 11/26 Algoritmo MergeSort Entrada: Um vetor A[0 .. n - 1] de elementos ordenáveis Saída: Vetor A[0 .. n – 1] ordenado de maneira crescente 1. se n > 1 então 2. Copiar A[0 .. ⌊n/2⌋ - 1] para B[0 .. ⌊n/2⌋ - 1] 3. Copiar A[⌊n/2⌋ .. n - 1] para C[0 .. ⌊n/2⌋ - 1] 4. MergeSort(B[0 .. ⌊n/2⌋ - 1]) 5. MergeSort(C[0 .. ⌊n/2⌋ - 1]) 6. Mesclar(B, C, A) VOCÊ SABIA? O número de comparações de chaves feitas pelo Merge sort, no pior caso, chega muito perto do mínimo teórico (log n! ≈ nlog n – 1,44n) [ #PraCegoVer : logari tmo de n fatorial na base 2 é aproximadamente igual a n log n na base 2 menos 1,44 n] que qualquer algoritmo de ordenação geral baseado em comparação pode ter. Para n grande, a quantidade de comparações feitas por esse algoritmo, no caso médio, acaba tendo cerca de 0,25n menos operações (LEVITIN, 2012). No algoritmo para mesclagem de vetores utilizado pelo Merge sort, o vetor A resultado é percorrido usando o índice k , e os subvetores com os índices i e j . Todos os três vetores são percorridos da esquerda para a direita. O laço na linha 3 implementa a varredura para construção do vetor de saída. Na primeira iteração, o procedimento identifica o elemento mínimo em L ou R e realiza sua cópia para a primeira posição do vetor de saída A. O elemento mínimo global está localizado ou em L (nesse caso é L[0], uma vez que L está ordenado) ou em R (nesse caso é R[0], uma vez que R está ordenado). Avançando o índice apropriado (i ou j), o algoritmo evita, definitivamente, de avaliar o elemento recém-copiado em uma iteração posterior. Então, o processo é repetido para identificar o menor elemento remanescente em L ou R (o segundo menor global). Em geral, o menor elemento ainda não copiado para A é L[ i ] ou R[ j ]; o procedimento 2 2 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 12/26 verifica explicitamente qualé a menor e prossegue adequadamente. Uma vez que cada iteração realiza a cópia do menor elemento ainda em avaliação em L ou R, o vetor de saída é completamente preenchido de maneira ordenada. Merge sort – vantagem vs. desvantagem » Clique nas abas para saber mais sobre o assunto Algoritmo Mesclar Entrada: Vetores B e C, ambos ordenados e de tamanho ⌊n/2⌋ Saída: Vetor A[0 .. n - 1] ordenado composto dos elementos de L e R 1. i ←0 2. j ←0 3. para k ←0 até n - 1 faça 4. se L[ i ] < R[ j ] então 5. A[ k ] ← L[ i ] 6. i ← i + 1 7. senão 8. A[ k ] ← R[ j ] 9. j ← j + 1 Vantagem Desvantagem 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 13/26 Figura 1 – Operações executadas pelo procedimento de mesclagem do Merge sort. Fonte: CORMEN et al., 2009, p. 32. A Figura 1 ilustra o funcionamento do procedimento de mesclagem de vetores acionados pelo algoritmo Merge sort. A situação detalhada na figura corresponde ao momento em que o vetor L contém a sequência 2, 4, 5, 7 e o vetor R a sequência 1, 2, 3, 6. Ambos já foram ordenados e agora precisam ser mesclados no vetor A de saída. As posições destacadas no vetor A indicam os valores finais após a mesclagem de um par de elementos de L e R. Na Figura 1(a) as primeiras posições de L e R são comparadas e o menor valor (R[ 1 ] = 1) é copiado para A[ 9 ]. Após essas operações, o índice j de R é incrementado, assim como o índice k do vetor A. Esse mesmo processo se repete nas 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 14/26 Figuras 1(b)-(d), representando apenas as próximas três iterações do laço principal do procedimento. Ao final de todas as iterações referentes a essa mesclagem, o trecho A[ 9 .. 16 ] do vetor A conterá a sequência 1, 2, 2, 3, 4, 5, 6, 7. Figura 2 – Algoritmo Merge sort em ação. Fonte: SKIENKA, 2008, p. 121. A Figura 2 apresenta uma execução completa do algoritmo Merge sort ordenando a sequência M, E, R, G, E, S, O, R, T. Como o algoritmo constitui um típico exemplo de aplicação da estratégia de divisão e conquista, a análise do seu comportamento assintótico pode ser feita com base nos passos abrangidos por essa estratégia: Divisão: a etapa de divisão apenas computa a posição correspondente à metade do vetor, o que demanda um tempo constante Θ(1) [ #PraCegoVer : t theta de 1]. Conquista: o algoritmo recursivamente resolve dois subproblemas, cada qual de tamanho n /2, o que agrega 2T( n /2) ao seu tempo de execução. 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 15/26 Combinação: o procedimento de mesclagem é responsável por esta etapa. Como descrito pelo pseudocódigo acima, esse algoritmo percorre um vetor de tamanho n cada vez que é invocado, o que demanda um tempo Θ(n) [ #PraCegoVer : theta de n]. Reunindo os custos relacionados a cada uma das etapas do algoritmo, é possível identificar a seguinte recorrência para descrever a complexidade do Merge sort: Aplicando o primeiro caso do teorema mestre para obtenção de uma solução fechada para essa recorrência, temos que a complexidade do algoritmo é da ordem de Θ(nlog(n)) [ #PraCegoVer : theta de n log de n]. Importante observar que, como o processo de particionamento do vetor em subvetores é baseado em posições e não em valores das chaves, o algoritmo tem a mesma complexidade tanto no pior como no melhor caso. 3.3 Complexidade do algoritmo quick sort no melhor caso Quicksort é o algoritmo de ordenação interna mais rápido que se conhece para uma ampla variedade de situações, sendo provavelmente mais utilizado do que qualquer outro algoritmo. De acordo com Ziviani (1999), o algoritmo foi inventado por C. A. R. Hoare em 1960, quando visitava a Universidade de Moscou como estudante. O algoritmo foi publicado mais tarde por Hoare (1962), após uma série de refinamentos. A ideia básica é a de particionar o problema de ordenação de um conjunto com n itens em dois problemas menores. A seguir, os problemas menores são ordenados independentemente, e depois os resultados são combinados para produzir a solução do problema maior. VOCÊ O CONHECE? Charles Antony Richard Hoare, aos 26 anos, inventou o algoritmo quick sort enquanto tentava ordenar palavras para o projeto de uma máquina de tradução do russo para o inglês. Ele fez a seguinte avaliação geral do seu trabalho (em tradução livre): “Tive muita sorte. Que maneira maravilhosa de começar uma carreira em computação, descobrindo um novo algoritmo de ordenação!” (HOARE, 1996). Vinte anos depois, ele recebeu o Prêmio Turing por 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 16/26 “contribuições fundamentais para a definição e projeto de linguagens de programação”. Em 1980, ele também foi nomeado cavaleiro por serviços à educação e à Ciência da Computação (LEVITIN, 2012). O quick sort, assim como o Merge sort, aplica uma estratégia de divisão e conquista. As três etapas dessa estratégia implementadas pelo algoritmo para a ordenação de uma sequência S de elementos são: 1. Divisão. Se S tem pelo menos dois elementos (nada precisa ser feito se S tem zero ou um elemento), selecionar um elemento x específico de S, o qual será chamado de pivô. Remova todos os elementos de S e os coloque em três outras sequências: ● L, contendo os elementos de S menores que x ; ● E, contendo os elementos de S iguais x ; ● G, contendo os elementos de S maiores que x . Naturalmente, se todos os elementos de S forem distintos, então E terá apenas um elemento. 2. Conquista. Recursivamente ordene as sequências L e G. 3. Combinação. Coloque os elementos de volta em S inserindo primeiro os elementos de L, depois os de E e, finalmente os de G. A Figura 3 apresenta uma visão esquemática do funcionamento do algoritmo. 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 17/26 Figura 3 – Visão esquemática do algoritmo quick sort. Fonte: GOODRICH; TAMASSIA; GOLWASSWER, 2014, p. 544. (Adaptado). O pseudocódigo a seguir apresenta os passos executados pelo algoritmo. A parte mais delicada deste procedimento é relativa à operação de partição (linha 2), a qual tem que rearranjar o vetor A[ esq .. dir ] através da escolha arbitrária de um item do vetor chamado pivô, de tal forma que ao final o vetor A está particionado em uma parte esquerda com chaves menores ou iguais ao pivô, e uma parte direita com chaves maiores ou iguais ao pivô. Naturalmente, depois que uma partição s é obtida, A[ s ] estará em sua posição final no vetor ordenado e, dessa forma, o processo de ordenação pode prosseguir para os dois subvetores à esquerda e à direita de A[ s ] independentemente. Algoritmo quick sort Entrada: Subvetor de um vetor A[ 0 .. n – 1] definido pelos seus índices esq esquerdo e 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 18/26 dir direito. Saída: Subvetor A[ esq .. dir ] ordenado de maneira crescente 1. se esq < dir então 2. pivo ← Partição(A[ esq .. dir ]) 3. QuickSort(A[ esq .. pivo - 1 ]) 4. QuickSort(A[ pivo + 1 .. dir ]) Quick sort vs Merge sort » Clique nas abas para saber mais sobre o assunto O procedimento de partição do vetor é detalhado no pseudocódigo a seguir. Várias estratégias podem ser adotadas para a escolha do elemento pivô. No algoritmo apresentado, foi utilizada a estratégia proposta por Ziviani (1999), em que o pivô é selecionado como sendo o elemento mais equidistantedas extremidades do vetor. O procedimento pode ser resumido nos seguintes passos: Escolha o elemento pivô segundo alguma estratégia (linha 03). Percorra o vetor a partir da esquerda até que um elemento A[ i ] ≥ A[ pivo ] seja encontrado (linha 05). Da mesma forma, percorra o vetor a partir da direita até que um elemento A[ i ] ≤ A[ pivo ] seja encontrado (linha 06). Como os dois elementos A[ i ] e A[ j ] estão fora do lugar no vetor final, troque os dois de posição (linha 08). Continue o processo até que os índices i e j se cruzem em algum ponto do vetor Ao final, o vetor A[ esq .. dir ] estará particionado da seguinte forma: Os elementos em A[ esq ], A[ esq + 1], …, A[ j ] serão menores ou iguais a A[ pivo ]. Os elementos em A[ i ], A[ i + 1], …, A[ dir ] serão maiores ou iguais a A[ pivo ]. Algoritmo Partição Entrada: Subvetor de um vetor A[ 0 .. n – 1] definido pelos seus índices esq esquerdo e Estratégia de Divisão Etapa de Maior Esforço 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 19/26 dir direito com esq < dir . Saída: Partição de A[ esq .. dir ] com a posição de pivô retornada pelo procedimento 1. i ← esq 2. j ← dir 3. pivo ← (i + j) / 2 4. enquanto i < j faça 5. enquanto A[ pivo ] > A [ i ] faça i ← i + 1 6. enquanto A[ pivo ] < A [ j ] faça j ← j - 1 7. se i ≤ j então 8. Trocar A[ i ] e A[ j ] de posições 9. i ← i + 1 10. j ← j - 1 11. retornar pivo Figura 4 – Operação de partição de um vetor. Fonte: Elaborado pelo autor, 2020. 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 20/26 A Figura 4 ilustra o funcionamento do procedimento de partição aplicado sobre o vetor com os elementos 85, 24, 63, 45, 17, 31, 96, 50. Na Figura 4(a), o elemento na posição 4 é selecionado como pivô. Na Figura 4(b), os elementos 85 (maior que 45) e 31 (menor que 45) são selecionados para troca. Na sequência, a Figura 4(c) indica o elemento 63 (maior que 45) e 17 (menor que 45) para a próxima troca. A Figura 4(d) apresenta o vetor após a conclusão do processo de partição quando os índices i e j se cruzam. Ordenação de arquivos com quick sort » Clique nas abas para saber mais sobre o assunto Considerando uma divisão o mais uniforme possível, o algoritmo de partição do quick sort produz dois subproblemas, cada um de tamanho não maior que n /2, uma vez que um tem tamanho ⌊ n/2 ⌋ [ #PraCegoVer : piso de n sobre 2] e outro tem tamanho ⌈ n /2⌉ - 1 [ #PraCegoVer : teto de n sobre 2 menos 1]. Nesse caso, o quick sort apresenta seu melhor desempenho e a recorrência que descreve o seu tempo de execução é dada por Aplicando o caso 2 do teorema mestre, temos que uma solução fechada para a recorrência pode ser expressa como T(n) = Θ(nlog (n)) [ #PraCegoVer : T de n é igual a theta de n log de n]. Portanto, ao equilibrar igualmente os dois lados da partição em cada nível da recursão, obtemos um algoritmo assintoticamente mais rápido. 3.4 Complexidade do algoritmo quick sort no pior caso Conforme apresentado previamente, o quick sort pode ser descrito por meio de quatro passos principais, e a escolha do elemento pivô é crítica para a eficiência do processo de ordenação. De acordo com Tenembaun, Langsam e Augenstein (1998), no Quick Sort original, o pivô era tomado como o elemento mais à esquerda do vetor a ser particionado. Nesse cenário, a boa eficiência do algoritmo depende de que o vetor original e todos os Memória e Operações Versão recursiva Implementação Estabilidade 2 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 21/26 subvetores resultantes não estejam ordenados, de modo que o valor do pivô sempre encontre a sua posição correta no meio do subvetor. Quick sort – passos gerais » Clique nas setas ou arraste para visualizar o conteúdo Supondo que as condições anteriores não se verifiquem e o vetor original esteja ordenado (ou quase ordenado), a posição onde o pivô se encontra vai impactar diretamente no desempenho da ordenação. Por exemplo, se o pivô estiver em sua posição correta, o vetor original será dividido em subvetores de tamanhos 0 e n - 1. Se esse processo continuar, um total de n - 1 subvetores será ordenado, sendo o primeiro de tamanho n , o segundo de tamanho n - 1, o terceiro de tamanho n - 2 e assim por diante. Pressupondo que sejam necessárias k comparações para reorganizar um vetor de tamanho k , o número total de comparações para ordenar o vetor inteiro será dado por: ELEMENTOS: PASSO 1 Se n = 0 ou n = 1, então o L está ordenado. PASSO 2 Escolha qualquer elemento (pivô) x em L. PASSO 3 Separe L - { x } em dois conjuntos disjuntos: A ordenação deve ser chamada recursivamente para S e S .1 2 PASSO 4 L recebe a concatenação de S , seguido de x, seguido de S .1 2 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 22/26 De maneira semelhante, se o vetor original estiver ordenado em ordem decrescente, a posição final do pivô (ainda assumindo que o escolhido seja o elemento na extremidade à esquerda do vetor) será na extrema direita do vetor e o vetor será novamente dividido em dois subvetores que ficarão muito desequilibrados – tamanhos n - 1 e 0. Portanto, o quick sort original apresenta a característica de funcionar melhor para vetores que estejam sem qualquer ordenação e pior para vetores completamente ordenados. VOCÊ SABIA? Uma característica relevante do quick sort é a sua ineficiência quando executado sobre arquivos já ordenados e quando a escolha do pivô é inadequada. Por exemplo, a escolha sistemática dos extremos de um arquivo já ordenado leva ao seu pior caso e o número de comparações passa a cerca de n /2 [ #PraCegoVer : n ao quadrado sobre 2]. Além disso, o tamanho da pilha auxiliar necessária para as chamadas recursivas é cerca de n (ZIVIANI, 1999). 2 Se for assumido que o particionamento do quick sort é feito de maneira desbalanceada (pior caso), o custo de executar a partição a cada chamada recursiva será da ordem de Θ(n) [ #PraCegoVer : theta de n]. Como a chamada recursiva sobre um vetor de tamanho 0 não realiza qualquer operação (o procedimento apenas retorna), temos que T(0) = Θ(1) [ #PraCegoVer : t de zero é igual a theta de um] e, portanto, a recorrência para o tempo de execução do algoritmo será dada por (CORMEN et al., 2009) A soma dos custos incorridos em cada nível da recursão será expressa pelo mesmo somatório já descrito previamente nessa seção, ou seja, a complexidade do pior caso do quick sort será da ordem Θ(n ) [ #PraCegoVer : theta de n ao quadrado]. Teste seus conhecimentos 2 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 23/26 Síntese Neste capítulo, tivemos a oportunidade de ampliar ainda mais o nosso conhecimento a respeito de recorrências. Embora existam outros métodos muito utilizados para a obtenção de soluções fechadas para funções desse tipo, o detalhamento do teorema mestre ofereceu uma nova ferramenta para a manipulação de recorrências. Os três casos contemplados por ele foram caracterizados dentro de exemplos práticos, o que possibilitou uma visão mais clara de como as condições requeridas para sua aplicação devem ser analisadas. Explorando ainda mais o conceito de recorrências, dois algoritmos de ordenação foram estudados: o Merge sort e o quick sort. Ambos foram avaliados em relação ao seu comportamento assintótico. Apesar da semelhança quanto à natureza recursiva, foi mostrado que esses algoritmos apresentam diferentes desempenhos na ordenação de chaves dentro da memória principal.Além disso, como o funcionamento de cada um foi bem explorado através de pseudocódigos, conseguimos formar um conhecimento para subsidiar a identificação dos cenários que sejam mais adequados para o empregado de cada um dos algoritmos. SAIBA MAIS Título : Estruturas de Dados e Algoritmos em Java Autores : Roberto Tamassia e Michael T. Godrich Atividade não pontuada. 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 24/26 Editora : Grupo A Ano : 2011 Comentário : Concentre-se no Capítulo 11, onde os algoritmos Merge sort e quick sort são bem explorados. Além disso, você vai encontrar implementações desses algoritmos na linguagem de programação Java. Isso possibilitará uma visão mais concreta de como trabalhar com esses algoritmos na prática. Onde encontrar : Biblioteca Virtual da Título : Estruturas de dados e seus algoritmos Autor : Jayme Luiz Szwarcfiter Editora : LTC Ano : 2011 Comentário : Explore o Capítulo 7 onde o funcionamento de vários algoritmos de ordenação é detalhado, além de todos eles serem analisados em seu comportamento assintótico. Em particular, os algoritmos Merge sort e quick sort são descritos de maneira muito didática. Onde encontrar: Biblioteca Virtual da Laureate Título : Linguagens Formais e Autômatos Autor(a) : Paulo F. B. Menezes Editora : Grupo A Ano : 2011 Comentário : Fique atento ao Capítulo 1, onde vários conceitos e definições matemáticas são apresentadas como subsídio para uma 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 25/26 melhor compreensão da ideia de recursividade. Algumas técnicas de demonstração também são exploradas e elas são muito úteis para a formalização de resultados computacionais. Onde encontrar? Biblioteca Virtual da Laureate Referências bibliográficas 08/09/2022 22:02 Unidade 3 - Análise de algoritmos https://codely-fmu-content.s3.amazonaws.com/Moodle/EAD/Conteudo/CTI_ANLALG_21/unidade_3/ebook/index.html?redirect=1 26/26 CORMEN, T. H. et al. Introduction to Algorithms . Cambridge: Massachusetts Institute of Technology, 2009. GOODRICH, M. T.; TAMASSIA, R.; GOLWASSWER, M. Data Structures & Algorithms . New Jersey: John Wiley & Sons, 2014. HOARE, C. A. R. Quicksort. Great Papers in Computer Science , pp. 31-39, 1996. Disponível em: < http://comjnl.oxfordjournals.org/content/5/1/10.full.pdf >. Acesso em: 6 jan. 2021. LEVITIN, A. Introduction to the design and analysis of algorithms . Boston: Addison- Wesley, 2012. ROUGHGARDEN, T. Algorithms Illuminated – Part 1: The Basics. San Francisco: Soundlikeyourself Publishing, 2017. SKIENKA, S. S. The Algorithm Design Manual . Nova York: Springer, 2008. SZWARCFITER, J. L.; MARKENZON, L. Estruturas de Dados e Seus Algoritmos . Rio de Janeiro: LTC, 2015. TENEMBAUN, A. A.; LANGSAM, Y.; AUGENSTEIN, M. J. Data Structures Using C . Nova York: McGraw-Hill, 1998. ZIVIANI, N. Projeto de Algoritmos com Implementações em Pascal e C . São Paulo: Pioneira Informática, 1999. http://comjnl.oxfordjournals.org/content/5/1/10.full.pdf
Compartilhar