Aula 03 - Correção de Algoritmos e Invariantes
Universidade Federal do Rio Grande do Sul
Instituto de Informática
Departamento de Informática Teórica
Estes slides utilizam conteúdo adaptado da bibliografia da disciplina e também de notas de aula e slides prévios dos professores Bruno Grisci, Rodrigo Machado e André Grahl.
“
Uma demonstração de correção convincente será impossível enquanto o algoritmo seja visto como uma caixa preta, nossa única esperança é não considerar o algoritmo como uma caixa preta.
— Edsger W. Dijkstra
“Notes On Structured Programming”, 1970
Def. Um algoritmo é qualquer procedimento computacional bem definido que recebe um conjunto de valores como entrada e produz um conjunto de valores como saída.
Obs. Um algoritmo pode ser visto como uma ferramenta para resolver um problema.
Def. Um algoritmo resolve um problema se para toda entrada:
- Ele pára.
- Retorna a saída correta.
P. Um algoritmo que não pára é útil? Um algoritmo incorreto é útil?
Problema. Especificação abstrata e genérica da relação entre um conjunto de entradas válidas e as saídas desejadas.
Instância. Um caso concreto ou entrada específica (com dados fixos) que satisfaz as restrições do problema.
Exemplo de Problema:
Ordenação de Vetores
Exemplo de Instância:
Uma entrada específica
Obs. Um algoritmo correto deve produzir a solução esperada para todas as instâncias possíveis do problema.
Algumas técnicas importantes para provar a correção de algoritmos:
Além da resposta do algoritmo estar correta, é também necessário estipular a garantia de terminação do algoritmo (i.e. variantes para laços)
Problema do Troco (Greedy Cashier’s Algorithm):
Emparelhamento Estável (Gale-Shapley):
Entrada. Uma sequência de k números \langle a_1,a_2, \dots, a_k \rangle.
Objetivo. Encontrar uma permutação \langle a_1',a_2', \dots, a_k' \rangle em que a_1' \leq a_2' \leq \dots \leq a_k'.
Instância: \langle 31,41,59,26,41,58 \rangle
Solução. \langle 26, 31, 41, 41, 58, 59 \rangle.
Funciona da forma que um humano organiza uma mão jogando cartas:

Obs. Nos pseudocódigos de todos os algoritmos desta aula, assumimos indexação de 1 a n (para vetores de tamanho n).
Def. Uma execução de um algoritmo é uma sequência de estados com a propriedade de que:
- Tem um estado inicial; e
- Se q e r são estados consecutivos na sequência, então q \longrightarrow r.
Def. Um estado é alcançável se aparece em alguma execução.
Def. Uma invariante é uma propriedade \textcolor{#2e7d32}{P} para estados:
- Tal que se \textcolor{#2e7d32}{P}(q), o estado q possui a propriedade \textcolor{#2e7d32}{P}; e
- q \longrightarrow r; então
- \textcolor{#2e7d32}{P}(r), o estado r possui a propriedade \textcolor{#2e7d32}{P}.
Def. Uma invariante é uma propriedade \textcolor{#2e7d32}{P} para estados:
- Tal que se \textcolor{#2e7d32}{P}(q), o estado q possui a propriedade \textcolor{#2e7d32}{P}; e
- q \longrightarrow r; então
- \textcolor{#2e7d32}{P}(r), o estado r possui a propriedade \textcolor{#2e7d32}{P}.
Princípio da Invariante. Se o estado inicial possui a invariante, então todos os estados alcançáveis possuem a invariante.
Item 1. Define a propriedade.
Item 2. Prova que a propriedade se mantém para q \longrightarrow r.
Item 3. Prova que a propriedade se mantém para qualquer estado alcançável.
Item 4. Prova que o algoritmo termina.
Item 5. Conclui que o algoritmo resolve o problema.
Invariante do Laço. No início de cada iteração do laço
For, se x está presente no vetor A, então x está presente no subvetor A[i \dots n].
Terminação e Corretude.
Def. Ordenado(q): A subsequência representada pelo estado q de j-1 números \langle a_1', a_2', \dots, a_{j-1}'\rangle obedece a restrição de que a_1' \leq a_2' \leq \dots \leq a_{j-1}'.
Lema 1. Para qualquer transição q \longrightarrow r do algoritmo de Ordenação por Inserção, se Ordenado(q) então Ordenado(r).
Prova. A demonstração do lema segue da construção do loop while.
Durante a execução do loop, o algoritmo move para a direita A[j - 1], A[j - 2], A[j - 3], e assim por diante, até que a posição correta de key é encontrada.
Nesse ponto key é colocada na posição correta e a subsequência está ordenada.
\blacksquare
Lema 2. O algoritmo de Ordenação por Inserção termina.
Prova. O loop externo termina quando j > n, isso ocorre quando j > A.length e j é apenas incrementado durante o algoritmo.
\blacksquare
Corolário. O algoritmo de Ordenação por Inserção resolve o problema de ordenação.
Prova. Pelo Lema 2, sabemos que o algoritmo termina depois de n iterações. Pelo Lema 1 sabemos que toda subsequência alcançada pelo algoritmo está ordenada, como na iteração n+1 a subsequência corresponde a todo o vetor, então podemos concluir que o algoritmo resolve o problema.
\blacksquare
Item 1. Define a propriedade.
Item 2. Prova que a propriedade se mantém para q \longrightarrow r.
Item 3. Prova que a propriedade se mantém para qualquer estado alcançável.
Item 4. Prova que o algoritmo termina.
Item 5. Conclui que o algoritmo resolve o problema.
Qual é o invariante do laço no algoritmo a seguir?
Exercício. Determine a propriedade invariante do laço
Fore o argumento de terminação.
Solução.
- Invariante do laço: No início de cada iteração i (1 \le i \le n+1), a variável s contém a soma dos elementos da subsequência A[1 \dots i-1]: s = \sum_{k=1}^{i-1} A[k]
- Base (i=1): s = 0 (soma de conjunto vazio).
- Passo (i \to i+1): adiciona-se A[i] a s, mantendo s = \sum_{k=1}^{i} A[k].
- Terminação: O laço termina quando i = n+1. Pelo invariante, s = \sum_{k=1}^{n} A[k], ou seja, a soma de todo o vetor A. \blacksquare
Qual é o invariante do laço no algoritmo a seguir?
Exercício. Determine a propriedade invariante do laço
Fore o argumento de terminação.
Solução.
- Invariante do laço: No início de cada iteração i (2 \le i \le n+1), a variável m armazena o maior valor presente no subvetor A[1 \dots i-1]: m = \max(A[1 \dots i-1])
- Base (i=2): m = A[1] = \max(A[1 \dots 1]).
- Passo (i \to i+1): se A[i] > m, m é atualizado para A[i], mantendo m = \max(A[1 \dots i]).
- Terminação: O laço termina quando i = n+1. Pelo invariante, m = \max(A[1 \dots n]), garantindo que m é o maior elemento de todo o vetor A. \blacksquare
Qual a intuição por trás da construção do SelectionSort?
Determine:
Qual a intuição por trás da construção do BubbleSort?
Determine:
Invariante do laço: No início da iteração i do laço externo (1 \le i \le n-1), os i-1 maiores elementos do vetor original já estão em suas posições finais, ocupando exatamente as posições A[n-(i-1)+1], A[n-(i-1)+2], \dots, A[n], isto é, o subvetor A[n-i+2 \dots n], e esses elementos estão ordenados.
Terminação: O laço externo executa para i=1,2,\dots,n-1. Quando ele termina, os n-1 maiores elementos estão em suas posições finais (A[2],\dots,A[n]). O único elemento restante ocupa A[1], portanto todo o vetor A está ordenado. \blacksquare
Obs. \textit{start} e \textit{end} representam os índices de início e fim da parte do vetor a ser ordenado. Para ordenar o vetor inteiro, chame \text{MergeSort}(A, 1, \text{length}(A)).
Para determinar a correção do MergeSort utilizamos indução estrutural em sequências de números.
QuickSort é outro algoritmo famoso e bastante utilizado.
Obs. A rotina \text{Partition} executa ao mesmo tempo a extração do pivô e o particionamento dos demais valores, devolvendo o índice p do pivô. Os elementos anteriores a p são menores que o pivô, e os posteriores a p, maiores. Para ordenar todo o vetor, a chamada inicial é \text{QuickSort}(A, 1, \text{length}(A)).
Tal qual no caso do MergeSort, utilizamos indução para demonstrar a correção do QuickSort.
Determine:
A verificação formal de software utiliza invariantes de laço, pré/pós-condições e Lógica de Hoare para provar matematicamente que programas atendem a suas especificações:
invariant) e pré/pós-condições (requires/ensures), provando a corretude de algoritmos automaticamente via Z3.loop invariant) para comprovação matemática de propriedades e ausência de bugs.?