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

Invariante de Laço

Invariante de Laço

Introdução

O conceito de invariante de laço é bastante importante e pode nos ajudar a analisar programas, verificar erros, e derivar programas a partir de especificações.

O invariante de laço pode ser definido como uma relação entre as variáveis de um algoritmo (programa) que é verdadeira nas seguintes condições:

Para ilustrar o conceito de invariante de laço, vamos considerar o seguinte algoritmo que computa a soma dos elementos de um array de inteiros.





\begin{algorithmic}
\par\STATE {\bf int Soma(int A[])}
\STATE $s \leftarrow 0$\S...
... + A[i]$ \STATE $i \leftarrow i + 1$\ENDWHILE
\STATE return $s$\end{algorithmic}

É possível identificar invariantes de laço nesse exemplo?

Ainda sobre Laços

Em um laço é possível identificar dois elementos:

Um laço está correto se as seguintes 5 condições são satisfeitas:

  1. Início: O invariante é verdadeiro antes de entrar no laço.
  2. Invariância: O corpo do laço preserva o invariante.
  3. Progresso: O corpo do laço diminui a variante.
  4. Limitação: Quando a variante atinge certo valor, a guarda se torna falsa.
  5. Saída: A negação da guarda e o invariante descrevem o objetivo do laço.

Verificar no exemplo se as 5 condições são satisfeitas.

Um outro exemplo

O seguinte algoritmo computa $x^ n$ através de multiplicações sucessivas. Determine os invariantes de laço.





\begin{algorithmic}
\par\STATE {\bf int Potencia(int $x$, int $n$)}
\STATE $p \l...
...times x$ \STATE $i \leftarrow i + 1$\ENDWHILE
\STATE return $p$\end{algorithmic}

Derivando Laços em um Algoritmo

A partir de invariantes, é possível derivar laços em um algoritmo. Para tanto, vamos considerar um padrão que relaciona as instruções de um laço:





\begin{algorithmic}
\par\STATE $PRE$\STATE $\rhd$\ Invariante é verdadeiro
\WHIL...
...ATE $\rhd \neg G \wedge $\ Invariante é verdadeiro
\STATE $POS$\end{algorithmic}

$G$ é a guarda do laço, $PRE$ é o código que precede o laço, $C$ corresponde ao corpo do laço e $POS$ é o código que vem depois do laço.

Para ilustrar a derivação de laços, vamos considerar a definição da divisão de inteiros que relaciona as saídas $q$ (quociente) e $r$ (resto) às entradas $n$ (número) e $d$ (divisor). Por definição, sabemos que $r < d$ e $n = q \times d + r$.

De fato, a definição da divisão corresponde a pós-condição do laço (aquilo que é verdade após a execução do laço). Sabemos que na pós-condição, $\rhd \neg G \wedge $ Invariante é verdadeiro. É fácil perceber que $\neg G = r < d$ e o invariante é $n = q \times d + r$. Logo, a guarda $G$ é $r \geq d$. Substituindo no padrão definido acima temos:





\begin{algorithmic}
\par\STATE $PRE$\STATE $\rhd$\ $n = q \times d + r$\WHILE{$ ...
...\rhd \neg (r \geq d) \wedge $\ $n = q \times d + r$\STATE $POS$\end{algorithmic}

Devemos derivar ainda $PRE$ e $C$, uma vez que $POS$ pode ser (return $q$). A idéia básica é contar quantas vezes nós subtraímos o divisor $d$ do numerador $n$. O quociente $q$ é o contador e o resto $r$ é o que sobra após a subtração. É justo considerar que inicialmente não fizemos nenhuma subtração. É importante ainda lembrar que o invariante deve ser válido antes do laço. Logo, se iniciamos $q \leftarrow 0$, indicando que não fizemos ainda nenhuma subtração, devemos fazer $r \leftarrow n$ para garantir o invariante.

Para definir o corpo do laço, devemos presevrar o inavariante. Logo, para cada subtração, o contador $q$ é incrementado de $1$ e o divisor subtraído do resto ( $r \leftarrow r - d$).

O algoritmo derivado é, portanto,





\begin{algorithmic}
\par\STATE $r \leftarrow n$\STATE $q \leftarrow 0$\STATE $\r...
...neg ( r \geq d) \wedge $\ $n = q \times d + r$\STATE return $q$\end{algorithmic}



Jorge C. A. de Figueiredo 2003-11-19