Aula 03 - Correção de Algoritmos. 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, André Grahl Pereira, Lucas Nunes Alegre, Marcus Ritt e Luciana Buriol.
“
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?
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 a 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:
Def. Um estado é alcançável se aparece em alguma execução.
Def. Uma invariante é uma propriedade \textcolor{#2e7d32}{P} para estados.
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.
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 Teorema 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
MergeSort é um dos mais famosos e eficientes algoritmos de ordenação baseado em comparações.
A sua ideia fundamental é uma rotina que une duas listas ordenadas em uma lista maior ordenada (merge).
O algoritmo divide a entrada ao meio até chegar ao caso base (vetor de tamanho 1 – trivialmente ordenado), e após recombina os vetores ordenados.
No pseudocódigo acima, as rotinas \text{FirstHalf} e \text{SecondHalf} extraem a primeira e segunda metade do vetor, respectivamente, e \text{Merge} une dois vetores ordenados em um único vetor ordenado.
Implementações eficientes de MergeSort requerem o uso de um vetor auxiliar e manipulação de índices (para que os cortes tenham custo constante).
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:
?