Análise e Técnicas de Algoritmos
Período 2003.2

Divisão-e-Conquista


Divisão e Conquista

Introdução

A maioria dos algoritmos de divisão e conquista divide o problema em $a$ subproblemas da mesma natureza, de tamanho $n/b$. Se $g(n)$ for o tempo requerido para decompor o problema em partes iguais e depois combinar o resultado, a complexidade de tempo pode ser determinada pela resolução da seguinte relação de recorrência: $t(n) = a.t(n/b) + g(n)$.

É possível utilizarmos o Teorema Master para resolver a recorrência.

Além da forma tradicional que vimos quando estudamos resolução de relação de recorrência, o Teorema Master pode ser reescrito da seguinte forma:

Se existir um $k$ onde $g(n) \in \Theta(n^k)$ então,


\begin{displaymath}t(n) \in \left\{ \begin{array}{ll}
\Theta(n^k) & \mbox{se $a...
...heta(n^{\log _b^a}) & \mbox{se $a > b^k$}
\end{array} \right. \end{displaymath}

Existe um grande número de problemas computacionais que podem ser resolvidos eficientemente por divisão e conquista. Na verdade, esta é uma das primeiras técnicas (excetuando a solução ingênua ou força bruta) que é tentado para a solução eficiente de um problema.

Entretanto, nem todos os problemas admitem uma solução eficiente por divisão e conquista. Em alguns casos, a solução por divisão-e-conquista apresenta a mesma complexidade assintótica de tempo que a solução ingênua. Em muitos destes casos, porém, ainda é uma estratégia interessante:

  1. Requer um número menor de acessos à memória.
  2. São altamente paralelizáveis. Se existem vários processadores disponíveis, a estratégia propicia eficiência.

Existem três condições que indicam que a estratégia de divisão-e-conquista pode ser utilizada com sucesso:

  1. Deve ser possível decompor uma instância em sub-instâncias.
  2. A combinação dos resultados deve ser eficiente.
  3. As sub-instâncias devem ser mais ou menos do mesmo tamanho.

É possível identificar pelo menos duas situações genéricas em que a abordagem por divisão-e-conquista é adequada:

  1. Problemas onde um grupo de operações são correlacionadas ou repetidas. A multiplicação de matrizes, que veremos a seguir, é um exemplo clássico.
  2. Problemas em que uma decisão deve ser tomada e, uma vez tomada, quebra o problema em peças disjuntas. Em especial, a abordagem por divisão-e-conquiasta é interessante quando algumas peças passam a ser irrelevantes.

Exemplos

Exemplo: Achar Maxim

O problema consiste em encontrar o maior elemento de um array $A[1..n]$.




Solução Ingênua


\begin{algorithmic}
\STATE
\STATE
\STATE $max = A[1];$\FOR{i = 2 to n}
\IF{A[i] $\rangle$\ max}
\STATE max = A[i]
\ENDIF
\ENDFOR
\end{algorithmic}




A solução ingênua é claramente $O(n)$.




Solução por Divisão e Conquista

  1. Divisão: Dividir o array em duas partes (de mesmo tamanho).
  2. Conquista: Achar o maior valor em cada um dos sub-arrays.
  3. Combinação: Determinar o maior valor entre os dois valores encontrados.


\begin{algorithmic}
\STATE
\STATE
\STATE {\bf function} Maxim(x, y)
\STATE
\IF{ ...
...y)/2 + 1, y)
\STATE {\bf return} max(max1, max2)
\ENDIF
\STATE
\end{algorithmic}

A análise do algoritmo que utiliza divisão e conquista requer a solução da seguinte relação de recorrência:

Neste caso, $a = b = 2$, $g(n) \in \Theta(1)$. Logo, $k = 0$.


\begin{algorithmic}
\STATE
\STATE Como $2 > 2^0$.
\STATE $T(n) \in \Theta (n^{\log _2^2})$\STATE $T(n) \in \Theta (n)$\STATE
\STATE
\end{algorithmic}

O método de divisão e conquista também produz um algoritmo com $O(n)$.

Exemplo: Busca Binária

O problema consiste em encontrar o índice do elemento $x$ em um array ordenado. Vamos assumir que o elemento $x$ sempre existe.

A solução ingênua para este problema é fazer uma busca sequencial do elemento no array. Parar quando o elemento $x$ for encontrado. É trivial observar que este algoritmo é $O(n)$.

A solução por divisão e conquista:

  1. Divisão: Comparar com o elemento médio.
  2. Conquista: Buscar $x$ na metade esquerda ou metade direita.
  3. Combinação: Trivial.


\begin{algorithmic}
\STATE
\STATE
\STATE {\bf function} Busca(A, x, l, r)
\STATE...
... \ELSE
\STATE Busca$(A, x, m + 1, r)$\par\ENDIF
\ENDIF
\STATE
\end{algorithmic}

A análise do algoritmo que utiliza divisão e conquista requer a solução da seguinte relação de recorrência:

Neste caso, $a = 1, b = 2$ e $k = 0$.


\begin{algorithmic}
\STATE
\STATE Como $1 = 2^0$.
\STATE $T(n) \in \Theta (n^k.\log n)$\STATE $T(n) \in \Theta (\log n)$\STATE
\STATE
\end{algorithmic}

A busca binária é mais eficiente do que uma busca sequencial, quando $n$ é grande.

Exemplo: A Função Power

Solução Alternativa por Divisão e Conquista


\begin{displaymath}a^n = \left\{ \begin{array}{ll}
a^{n/2}.a^{n/2} & \mbox{se $...
....a^{n-1/2}.a & \mbox{se $n$\ é ímpar} \\
\end{array} \right. \end{displaymath}

A relação de recorrência é, portanto,

$T(n) = T(n/2) + \Theta (1) \Rightarrow T(n) = \Theta (\log n)$.

Exemplo: Multiplicação de Inteiros Grandes

Solução Alternativa por Divisão e Conquista

\epsffile{mpy.eps}

A solução por divisão e conquista:

  1. Divisão: Dividir cada número em dois números com metade da quantidade de dígitos.
  2. Conquista: Proceder a multiplicação das quatro partes.
  3. Combinação: Combinar os resultados com deslocamento e adições.

A análise do algoritmo que utiliza divisão e conquista requer a solução da seguinte relação de recorrência:

Neste caso, $a = 4, b = 2$ e $k = 1$.


\begin{algorithmic}
\STATE
\STATE Como $4 > 2^1$.
\STATE $T(n) \in \Theta (n^{\log _2^4})$\ (caso 3)
\STATE $T(n) \in \Theta (n^2)$\STATE
\STATE
\end{algorithmic}

Essa solução por divisão e conquista não apresenta vantagem em relação à solução original de multiplicação de inteiros.

Uma Solução por Divisão e Conquista Mais Eficiente


\begin{algorithmic}
\STATE
\STATE $C = mult(w, y)$.
\STATE $D = mult(x, z)$.
\ST...
...E Logo, teremos: $mult(A, B) = C.10^{2m} + E.10^m + D$.
\STATE
\end{algorithmic}

No total, fazemos 3 multiplicações, 4 adições e 2 subtrações de números com $n/2$ dígitos. É necessário ainda fazer deslocamentos mas, vimos que tudo isso representa $\Theta (n)$.

Logo, a análise do algoritmo que utiliza divisão e conquista requer a solução da seguinte relação de recorrência:

Neste caso, $a = 3, b = 2$ e $k = 1$.


\begin{algorithmic}
\STATE
\STATE Como $3 > 2^1$.
\STATE $T(n) \in \Theta (n^{\l...
...)$\ (caso 3)
\STATE $T(n) \in \Theta (n^{1.585})$\STATE
\STATE
\end{algorithmic}

Essa solução é realmente mais eficiente? Já estudamos isso. Para $n$ muito grande, essa solução é muito mais eficiente.

Exemplo: Multiplicação de Matrizes

O algoritmo ingênuo:


\begin{algorithmic}
\STATE
\STATE
\STATE {\bf function} MultMatriz(X, Y, Z, n)
\...
...] = X[i, j] + Y[i, k].Z[k, j]$ \ENDFOR
\ENDFOR
\ENDFOR
\STATE
\end{algorithmic}

Considerando que cada operação de inteiros é $\Theta (1)$, o algoritmo ingênuo é $\Theta (n³)$.

Solução Alternativa por Divisão e Conquista


\begin{displaymath}X = \left[ \begin{array}{ll}
I & J \\
K & L
\end{array} \right] \end{displaymath}


\begin{displaymath}Y = \left[ \begin{array}{ll}
A & B \\
C & D
\end{array} \right] \end{displaymath}


\begin{displaymath}Z = \left[ \begin{array}{ll}
E & F \\
G & H
\end{array} \right] \end{displaymath}

Então,


\begin{algorithmic}
\STATE
\STATE $I = AE + BG$\STATE $J = AF + BH$\STATE $K = CE + DG$\STATE $L = CF + DH$\STATE
\end{algorithmic}

A análise do algoritmo que utiliza divisão e conquista requer a solução da seguinte relação de recorrência:

Neste caso, $a = 8, b = 2$ e $k = 2$.


\begin{algorithmic}
\STATE
\STATE Como $8 > 2^2$.
\STATE $T(n) \in \Theta (n^{\log _2^8})$\ (caso 3)
\STATE $T(n) \in \Theta (n^3)$\STATE
\STATE
\end{algorithmic}

Essa solução por divisão e conquista não apresenta vantagem em relação à solução original de multiplicação de inteiros.

Uma Solução por Divisão e Conquista Mais Eficiente


\begin{algorithmic}
\STATE
\STATE $M_1 = (A + C)(E + F)$\STATE $M_2 = (B + D)(G ...
... + D).E$\STATE $M_6 = (A + B).H$\STATE $M_7 = D.(G - E)$\STATE
\end{algorithmic}

Então,


\begin{algorithmic}
\STATE
\STATE $I = M_2 + M_3 - M_6 - M_7$\STATE $J = M_4 + M_6$\STATE $K = M_5 + M_7$\STATE $L = M_1 - M_3 - M_4 - M_5$\STATE
\end{algorithmic}

Logo, a análise do algoritmo que utiliza divisão e conquista requer a solução da seguinte relação de recorrência:

Neste caso, $a = 7, b = 2$ e $k = 2$.


\begin{algorithmic}
\STATE
\STATE Como $7 > 2^2$.
\STATE $T(n) \in \Theta (n^{\l...
...7})$\ (caso 3)
\STATE $T(n) \in \Theta (n^{2.8})$\STATE
\STATE
\end{algorithmic}

Para $n$ muito grande, essa solução é muito mais eficiente.



Jorge C. A. de Figueiredo 2003-12-09