Edson Prestes
Divisão e conquista Divide um problema em subproblemas independentes, resolve-os e combina as soluções obtidas em uma solução para o problema original. Isso resulta em um processo recursivo de decomposições e recombinações. Pode ser aplicado em problemas de: buscas em tabelas, como buscas seqüencial e binária; classificação, como classificação por seleção (selectionsort), por intercalação (mergesort) e por particionamento (quicksort); multiplicação (de matrizes e de números binários, por exemplo); seleção (para determinar máximo e mínimo, etc.). posicionamento de braço manipuladores; planejamento de caminhos em robótica móvel, etc
Somatório dos elementos de uma lista. Considere uma lista L de elementos do tipo inteiro. Se L tem no máximo 1 elemento, a soma de seus elementos é trivial. Caso contrário, este somatório pode ser visto como sendo a soma dos elementos da primeira metade de L, chamada L 1, com os elementos da segunda metade, chamada L 2. somatorio(l):= se curta( L ) // Tem comprimento de no máximo 1? então retorna ( L ) // retona zero se L é vazia, ou o próprio elemento. senão retorna (soma(somatorio(sublist1(l)), somatorio(sublist2(l)))). Onde soma(r 1, r 2 ) = r 1 + r 2
Classificação de listas por intercalação de sublistas. O algoritmo recebe como entrada uma lista L e devolve esta lista classificada. Se L tem comprimento no máximo 1, então L já está classificada. Caso contrário, a lista L é dividida em duas listas aproximadamente do mesmo tamanho, L 1 e L 2 correspondendo a primeira e a segunda metades de L, respectivamente. L 1 e L 2 são recursivamente classificadas e intercaladas a fim de obter uma versão classificada da lista de entrada.
Divisão e conquista se a entrada é simples, a saida é obtida diretamente; caso contrário, a entrada é decomposta e aplicado o mesmo processo. os resultados parciais são combinados para gerar uma saída para a entrada original. Baseado nisto, podemos descrever a divisão e conquista binária como Onde - as funções part1 e part2 decompõem a entrada; - a função cmbn_2 combina as saídas; - o procedimento smpl testa se a entrada é simples ou não; e - a função drt dá a saída para entradas simples.
O algoritmo Mergesort pode ser obtido especializando a formulação recursiva da divisão e conquista binária Mergesort( d ) := Div_Conq_2 ( d ) smpl( d ) := compr( d ) 1 {teste smpl} ; drt ( d ) := d {operação drt} ; part1( d ) := Prim(d) {primeira parte} ; part2( d ) := Fin(d) {segunda parte} ; cmbn_2( r1, r2 ) := Intercl( r1, r2 ) {combinação}
O algoritmo Somatório pode ser visto como uma especialização da formulação recursiva da divisão e conquista somatorio(l):= se simples( L ) então retorna ( L ) senão retorna (soma(somatorio(sublist1(l)), somatorio(sublist2(l)))). Div_Conq_2 ( d ) :=Somatório( L) smpl( d ) := simples(l) drt ( d ) := retorna(l) part1( d ) := sublist1(l) part2( d ) := sublist2(l) cmbn_2( r1, r2 ) := soma( r1, r2 )
A recursão na divisão e conquista pode ser visualizada, em três estágios : 1. Construção da árvore de dados: é construída da raiz em direção à folhas (por decomposições repetidas das instâncias de dados) até que as folhas sejam simples. 2. Aplicação da função drt para transferir as folhas dados para o estágio de resultados. 3. Construção da árvore de resultados: é construída das folhas em direção à raiz.
A execução do algoritmo Mergesort sobre a entrada d := [ 4, 3, 2, 1 ]
A formulação binária pode ser generalizada para uma versão m-ária. Um exemplo imediato de divisão e conquista ternária (m = 3) pode ser obtido, modificando o algoritmo Quicksort. Essa nova versão divide a entrada em três partes a partir do pivô: a primeira com os elementos menores do que o pivô, a segunda com os elementos iguais ao pivô, e a terceira com os elementos maiores do que o pivô. A combinação é feita através do processo de concatenação.
Podemos ter o caso de divisão e conquista unária (m = 1). Neste caso, o termo divisão é substituido por redução Exemplos: algoritmos de busca - Na busca seqüencial, a pesquisa é direcionada a uma tabela com um elemento a menos, caso o elemento procurado ainda não tenha sido encontrado. - Na busca binária, a pesquisa é direcionada a uma das metades da tabela, dependendo da comparação com o elemento procurado.
Projeto de Algoritmos por Divisão e Conquista Considere a versão binária Podemos ajustar esta função para gerar diferentes algoritmos de classificação. Considere que smpl e a função unária drt correspondam, respectivamente, a Smpl(d) = compr(d) 1 Drt (d):=d
As decomposições e recombinações podem ser definidas da seguinte maneira Versão 1 Part 1 e Part 2 : a primeira e a segunda metades da entrada d e Cmbn_2(r 1, r 2 ) : a intercalação de r 1 e r 2. Dá origem ao algoritmo Mergesort Versão 2 Part 1 pode conter o menor valor da entrada d e Part 2 pode ser a entrada d restante. Cmbn_2(r 1, r 2 ) pode ser a concatenação de r 1 seguida de r 2. Dá origem ao algoritmo de Seleção
Quais são as árvores de execução para a entrada [4,3,2,1]? Algoritmo Mergesort Algoritmo Seleção
Quais são as árvores de execução para a entrada [2,1,0,3,5,4]? Algoritmo Mergesort Algoritmo Seleção
O que podemos concluir? Quanto mais balanceado for o particionamento do(s) dado(s) de entrada menores serão as árvores de execução! De acordo com o princípio da Equipartição, o desempenho do algoritmo é dependente do balanceamento do particionamento. Ele tende a melhorar a medida que o particionamento se torna equilibrado.
Mínimo & Máximo em Tabelas Smpl Identifique as funções: Drt, Smpl, Part e Cmbn. Drt Part 1 : Tab[p p s ] Part 2 : Tab[q i q] Cmbn
Árvores de Execução para a Entrada [1,4,5,2,3,5]
Ocorrência em Tabela (ch, tab[p q])
As árvores de execução para a Entrada [10,41,25,2,3,5]
Mínimo de Tabela por Divisão e Conquista Dada uma tabela Tab o algoritmo deve determinar o menor elemento armazenado. Se a entrada é pequena ( Tab 1), a resposta é trivial. Caso contrário, o primeiro elemento de Tab é eliminado e calculado o menor valor existente no restante da tabela (SubTab). Após ter este valor ter sido determinado, ele é comparado com o elemento previamente eliminado.
As árvores de execução para a entrada (10,41,25,2,3,5) divisão e conquista unária liberal.
Análise da complexidade : Ocorrência em Tabela (ch, tab[p q])
O Algoritmo recebe como entrada um par ordenado: uma chave de pesquisa e uma tabela tab retorna como saída um valor verdadeiro ou falso O tamanho da entrada corresponde a dimensão n da tabela tab. A operação fundamental é a comparação (Tab[p]=ch) da chave com os elementos da tabela Tab Quantas chamadas recursivas são realizadas? Quantas vezes a entrada d teve que ser particionada até smpl ser verdadeiro?
smpl drt part1 cmbn
As chamadas recursivas recebem como entrada um dado cujo tamanho varia da seguinte maneira O que H(d) representa? Quanto vale H(d)? Ele corresponde a altura da árvore de execução, a qual é no máximo n-1.
Qual é o desempenho do algoritmo Oc_Seq sobre a entrada d smpl Se smpl(d) (tam(d) 1), logo Desemp[Oc_seq] (d) = aval[smpl](d) + desemp[drt](d) drt part1 Se ~ smpl(d) (tam(d)>1), temos Desemp[Oc_seq] (d) = aval[smpl](d) + desemp[part 1 ](d) + Desemp[Oc_seq] (part 1 (d)) + cmbn Desemp[cmbn](Oc_seq(part 1 (d)))
Em relação ao número de comparações realizadas Se smpl(d) (tam(d) 1), temos Desemp[Oc_seq] (d) = aval[smpl](d) + desemp[drt](d) = 0+1 = 1 Se ~ smpl(d) (tam(d)>1), temos Desemp[Oc_seq] (d) = aval[smpl](d) + desemp[part 1 ](d) + Desemp[Oc_seq] (part 1 (d)) + Desemp[cmbn](Oc_seq(part 1 (d))) Desemp[Oc_seq] (d) = 0 + 1 + Desemp[Oc_seq] (part 1 (d)) + 0 = 1 + Desemp[Oc_seq] (part 1 (d))
Logo, o desempenho do algoritmo é descrito pela recorrência Portanto,
Análise da complexidade : Classificação de listas usando Mergesort
O Algoritmo recebe como entrada uma lista d de Elementos retorna como saída a lista d ordenada O tamanho da entrada corresponde à quantidade de elementos n da lista d. A operação fundamental corresponde a comparação entre os elementos da lista Quantas chamadas recursivas são realizadas? Quantas vezes a entrada d foi particionada até smpl ser verdadeiro?
A cada nível a entrada tem tamanho máximo de acordo com a seguinte tabela Onde A quantidade de chamadas H(d) é dependente de S(d)
O desempenho do algoritmo MergeSort sobre a entrada d é Se smpl(d) (tam(d) 1), logo Desemp[MergeSort] (d) = aval[tam(d) 1] + desemp[r d] = 0 + 0=0 Se ~ smpl(d) (tam(d)>1), temos Desemp[MergeSort](d)=aval[tam(d) 1]+ Desemp[d 1 Prim(d)] + Desemp[d 1 Fin(d)]+ Desemp[MergeSort](Prim(d))+ Desemp[MergeSort](Fin(d)) + Desemp[Intercl](MergeSort (Prim(d)), MergeSort (Fin(d))). =0+0+0+ Desemp[MergeSort](Prim(d))+ Desemp[MergeSort](Fin(d)) + tam(mergesort (Prim(d)))+ tam(mergesort (Fin(d)))-1.
Como a quantidade de elementos se mantém constante durante toda a execução temos que Prim(d) + Fin(d) = tam(d), logo Desemp[MergeSort](d)=Desemp[MergeSort](Prim(d))+ Desemp[MergeSort](Fin(d)) + tam(d)-1. Portanto, o desempenho do algoritmo em função do número de comparações é
A cota superior (CS) do Algoritmo é
Portanto