Invariante de Laç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.
É possível identificar invariantes de laço nesse exemplo?
Em um laço é possível identificar dois elementos:
Um laço está correto se as seguintes 5 condições são satisfeitas:
Verificar no exemplo se as 5 condições são satisfeitas.
O seguinte algoritmo computa através
de multiplicações sucessivas. Determine os invariantes de
laço.
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:
é a guarda do laço,
é o código que precede o laço,
corresponde ao corpo do laço e
é 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 (quociente) e
(resto) às entradas
(número) e
(divisor). Por definição, sabemos que
e
.
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, Invariante é verdadeiro. É
fácil perceber que
e o invariante é
. Logo, a guarda
é
. Substituindo no padrão
definido acima temos:
Devemos derivar ainda e
, uma vez que
pode ser (return
). A idéia
básica é contar quantas vezes nós subtraímos
o divisor
do numerador
. O quociente
é o contador e o resto
é 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
, indicando que não fizemos ainda
nenhuma subtração, devemos fazer
para garantir o invariante.
Para definir o corpo do laço, devemos presevrar o
inavariante. Logo, para cada subtração, o contador é incrementado de
e o divisor
subtraído do resto (
).
O algoritmo derivado é, portanto,