Indução estrutural e provas de programas

Conjuntos recursivos, invariantes de palavras, listas, concatenação, reverse e map.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

Na indução sobre naturais, o passo constrói n+1n+1 a partir de nn. Na indução estrutural, seguimos os construtores do objeto: uma palavra pode nascer de outra palavra, uma lista pode nascer de uma cabeça e uma cauda, uma expressão pode nascer de duas subexpressões. Assumimos a ideia de base e passo da lição anterior, aplicada a cada construtor.

Uma definição indutiva descreve os objetos obtidos por um número finito de aplicações das regras. Não inclui objetos adicionais só porque não foram explicitamente proibidos.

Indução num conjunto recursivo

Se SS é o menor conjunto com 3∈S3\in S e x,y∈S⇒x+y∈Sx,y\in S\Rightarrow x+y\in S, queremos mostrar:

S={3n:n∈N+}.S=\{3n:n\in\mathbb N^+\}.

Para S⊆{3n:n∈N+}S\subseteq\{3n:n\in\mathbb N^+\}, fazemos indução estrutural:

  • Base: 3=3⋅13=3\cdot1 é um múltiplo positivo de 3.
  • Construtor: se x=3ax=3a e y=3by=3b com a,b>0a,b>0, então x+y=3(a+b)x+y=3(a+b), também múltiplo positivo de 3.

Isto prova que as regras nunca produzem outro tipo de valor, mas ainda falta a inclusão inversa. Por indução no natural positivo nn, 3⋅1∈S3\cdot1\in S e, se 3n∈S3n\in S, então 3(n+1)=3n+3∈S3(n+1)=3n+3\in S; mostrar que todos os elementos construídos têm uma propriedade não prova que todos os objetos com essa propriedade se constroem, por isso verifica as duas direções numa igualdade de conjuntos.

Palavras e invariantes

Considera palavras sobre {a,b}\{a,b\} construídas por estas regras: a palavra vazia ε\varepsilon pertence a LL; se x∈Lx\in L, então axb,bxa∈Laxb,bxa\in L; se x,y∈Lx,y\in L, então xy∈Lxy\in L.

Para construir aabb, partimos de ε\varepsilon, aplicamos x↦axbx\mapsto axb e obtemos ab. Aplicamos o mesmo construtor a ab e obtemos aabb. Não existe nas regras uma operação que acrescente apenas um a; o par exterior entra numa única aplicação.

Vamos provar que cada palavra de LL tem tantos aa como bb. O caso base tem zero de cada. Se xx tem kk de cada, axbaxb e bxabxa têm k+1k+1 de cada. Se xx tem kk de cada e yy tem mm de cada, a concatenação tem k+mk+m de cada. Tratámos todos os construtores.

Uma propriedade que se conserva em cada construção chama-se invariante. Pode servir para mostrar que uma palavra não pertence ao conjunto: aaabb não pode pertencer a LL, porque tem contagens diferentes. Para mostrar que pertence, fornece uma construção a partir das bases.

Parênteses equilibrados

Se as regras são ()∈E()\in E, x∈E⇒(x)∈Ex\in E\Rightarrow(x)\in E e x,y∈E⇒xy∈Ex,y\in E\Rightarrow xy\in E, todas as expressões têm igual número de aberturas e fechos, mas podemos provar uma propriedade mais forte: em cada prefixo, o número de fechos não excede o de aberturas. Define o saldo como aberturas menos fechos.

  • Em (), os prefixos têm saldos 0, 1 e 0.
  • Em (x)(x), o primeiro parêntese acrescenta 1 ao saldo de cada prefixo de xx. O último fecha a abertura exterior.
  • Em xyxy, um prefixo fica em xx ou atravessa xx e entra em yy. No segundo caso, o saldo total de xx é zero e o saldo restante é o do prefixo de yy.

Contagens totais iguais não chegam para esta propriedade: )( tem uma abertura e um fecho, mas o seu primeiro prefixo já tem saldo negativo. Para ver a diferença, seguimos o saldo após consumir nn símbolos de cada palavra. n=0n=0 corresponde ao prefixo vazio; uma célula vazia indica que a palavra já terminou.

nnSaldo de (())Saldo de )(
000
11−1
220
31
40

O saldo final é zero nos dois casos. Só a primeira palavra mantém todos os prefixos com saldo não negativo. A definição de EE não inclui a palavra vazia: cada expressão começa em () e tem pelo menos dois símbolos, embora o prefixo vazio seja usado no teste do saldo.

Indução sobre listas finitas

Uma lista finita tem dois construtores: [], a lista vazia, e x:xs, uma cabeça x seguida da cauda xs. Para provar uma propriedade de todas as listas finitas:

  1. Prova o caso [].
  2. Para uma cabeça arbitrária x e uma cauda arbitrária xs, assume a propriedade de xs e prova-a para x:xs.

A hipótese pertence à cauda, não à lista inteira. Não usamos este princípio para listas infinitas de Haskell.

Em Haskell, f x aplica a função f a x; [x] abrevia x:[]; ++ concatena listas. As definições seguintes são equações para listas finitas:

length [] = 0
length (x:xs) = 1 + length xs

[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)

reverse [] = []
reverse (x:xs) = reverse xs ++ [x]

Na concatenação, a recursão analisa a primeira lista. Por isso, induzir nessa lista costuma permitir aplicar logo a equação recursiva.

Comprimento de uma concatenação

Queremos length (xs ++ ys) = length xs + length ys para quaisquer listas finitas. Fazemos indução em xs, mantendo ys arbitrária.

Na base:

length ([] ++ ys)
= length ys                        definição de ++
= 0 + length ys                    aritmética
= length [] + length ys            definição de length

No passo, a hipótese é length (xs ++ ys) = length xs + length ys:

length ((x:xs) ++ ys)
= length (x : (xs ++ ys))           definição de ++
= 1 + length (xs ++ ys)             definição de length
= 1 + length xs + length ys         hipótese de indução
= length (x:xs) + length ys         definição de length

Nomear a equação usada em cada passagem permite conferir a prova sem executar o programa.

Uma função acrescenta dois elementos por passo

Considere-se uma função que coloca um separador depois de cada elemento da lista:

interleave x [] = []
interleave x (y:ys) = y : x : interleave x ys

interleave 0 [2,3] dá [2,0,3,0]. O separador também aparece depois do último elemento. Queremos provar length (interleave x ys) = 2 * length ys para todas as listas finitas, com x arbitrário.

A base dá length [] = 2 * length [], isto é, 0 = 0. No passo, toma uma cabeça y e uma cauda ys. A hipótese só fornece a igualdade para essa cauda:

length (interleave x (y:ys))
= length (y : x : interleave x ys)  definição de interleave
= 1 + 1 + length (interleave x ys)  duas aplicações de length
= 2 + 2 * length ys                hipótese de indução
= 2 * (1 + length ys)              distributividade
= 2 * length (y:ys)                definição de length

A chamada recursiva recebe apenas ys; é aí que a hipótese se aplica. Mesmo quando x=y, foram acrescentadas duas posições, não uma: listas conservam repetições.

Associatividade da concatenação

Para (xs ++ ys) ++ zs = xs ++ (ys ++ zs), induzimos em xs, com ys,zs arbitrárias. Na base, ambos os lados reduzem a ys ++ zs; no passo:

((x:xs) ++ ys) ++ zs
= (x : (xs ++ ys)) ++ zs
= x : ((xs ++ ys) ++ zs)
= x : (xs ++ (ys ++ zs))            hipótese de indução
= (x:xs) ++ (ys ++ zs)

Também precisamos da identidade à direita xs ++ [] = xs. A base é [] ++ [] = []; no passo, (x:xs) ++ [] = x:(xs ++ []) = x:xs pela hipótese.

Reverse de uma concatenação

O lema é:

reverse (xs ++ ys) = reverse ys ++ reverse xs

Na base xs=[], o lado esquerdo é reverse ys; o direito é reverse ys ++ [], igual pelo lema da identidade à direita. No passo:

reverse ((x:xs) ++ ys)
= reverse (x : (xs ++ ys))
= reverse (xs ++ ys) ++ [x]
= (reverse ys ++ reverse xs) ++ [x]   hipótese de indução
= reverse ys ++ (reverse xs ++ [x])   associatividade de ++
= reverse ys ++ reverse (x:xs)

É este lema que falta quando tentamos provar que inverter duas vezes recupera a lista:

reverse (reverse (x:xs))
= reverse (reverse xs ++ [x])
= reverse [x] ++ reverse (reverse xs)  lema anterior
= [x] ++ xs                          hipótese de indução
= x:xs

A base é reverse (reverse []) = []. Os lemas são necessários porque a hipótese de indução só fala de reverse (reverse xs), não de reverse (reverse xs ++ [x]).

Fusão de map

map f aplica f a cada elemento. A composição (f . g) x é f (g x):

map f [] = []
map f (x:xs) = f x : map f xs
(f . g) x = f (g x)

Podemos provar map f (map g xs) = map (f . g) xs por indução em xs. A base dá [] dos dois lados. No passo:

map f (map g (x:xs))
= map f (g x : map g xs)
= f (g x) : map f (map g xs)
= (f . g) x : map (f . g) xs         hipótese e definição de composição
= map (f . g) (x:xs)

A igualdade permite juntar duas travessias numa. A prova explica por que a transformação conserva os valores para todas as listas finitas, sem depender de exemplos escolhidos.

Provar que uma ordenação ordena

Considere-se a ordenação por inserção de elementos com uma ordem total:

insert x [] = [x]
insert x (y:ys)
  | x <= y = x:y:ys
  | otherwise = y : insert x ys

sort [] = []
sort (x:xs) = insert x (sort xs)

Para provar que sort xs está ordenada, primeiro provamos o lema: inserir x numa lista ordenada conserva a ordenação.

A lista vazia produz [x], ordenada. Para y:ys ordenada, se x <= y, o resultado começa em x,y e conserva a cauda. Se x > y, aplicamos a hipótese à inserção na cauda. Os valores de ys são todos pelo menos y, e o novo valor x também é maior que y; logo, a cabeça y continua antes de uma cauda ordenada.

Na prova de sort, a base é vazia. No passo, a hipótese garante que sort xs está ordenada, e o lema garante que insert x (sort xs) também está.

Conservação das multiplicidades

Para um valor vv, seja conta⁡v(xs)\operatorname{conta}_v(xs) o número de ocorrências de vv na lista. Vale conta⁡v([])=0\operatorname{conta}_v([])=0 e

conta⁡v(y:ys)=1v=y+conta⁡v(ys),\operatorname{conta}_v(y:ys)=\mathbf 1_{v=y}+\operatorname{conta}_v(ys),

onde 1v=y\mathbf 1_{v=y} é 1 se v=yv=y e 0 no caso contrário. Queremos provar o lema

conta⁡v(insert⁡(x,ys))=conta⁡v(ys)+1v=x.\operatorname{conta}_v(\operatorname{insert}(x,ys)) =\operatorname{conta}_v(ys)+\mathbf 1_{v=x}.

Fazemos indução em ysys, com x,vx,v arbitrários. A base dá a lista [x][x], com exatamente 1v=x\mathbf 1_{v=x} ocorrências. No passo ys=y:zsys=y:zs, se x≤yx\le y, a saída é x:y:zsx:y:zs e contamos 1v=x+1v=y+conta⁡v(zs)\mathbf 1_{v=x}+\mathbf 1_{v=y}+\operatorname{conta}_v(zs). Se x>yx>y, a saída é y:insert⁡(x,zs)y:\operatorname{insert}(x,zs); pela hipótese, a contagem é 1v=y+conta⁡v(zs)+1v=x\mathbf 1_{v=y}+\operatorname{conta}_v(zs)+\mathbf 1_{v=x}. Ambas são a contagem original mais a ocorrência inserida.

Na indução sobre sort, a base conserva zero ocorrências. No passo, o lema acrescenta exatamente a ocorrência da cabeça xx às contagens de sort xs, que pela hipótese são as de xs. Assim, a contagem final coincide com a de x:xs para todo o vv.

Ordenação não é toda a correção: a função que devolve sempre [] também devolve uma lista ordenada. Para provar que é uma ordenação do conteúdo original, precisamos ainda de mostrar que conserva os elementos e as suas multiplicidades.

Exercícios

O conjunto gerado por uma regra

Seja SS o menor subconjunto de N\mathbb N que contém 3 e tal que x∈Sx\in S implica x+2∈Sx+2\in S. Prova S={2k+3:k∈N}S=\{2k+3:k\in\mathbb N\}.

Pista

Uma igualdade de conjuntos exige as duas inclusões.

Mais uma pista

Usa indução estrutural para S⊆TS\subseteq T e indução em kk para T⊆ST\subseteq S.

Solução

Base estrutural: 3=2⋅0+33=2\cdot0+3. Se x=2k+3x=2k+3, então x+2=2(k+1)+3x+2=2(k+1)+3, logo todo o elemento gerado está em TT. No sentido inverso, k=0k=0 dá 3. Se 2k+3∈S2k+3\in S, a regra dá 2(k+1)+3∈S2(k+1)+3\in S. Assim cada elemento de TT pertence a SS.

Confere a tua resposta:

  • Provei que tudo o que é gerado tem a forma indicada.
  • Provei que cada 2k + 3 pode ser gerado.
  • Usei o menor conjunto, não qualquer conjunto fechado pela regra.

Erros frequentes

Mostrar só que T contém 3 e é fechado pela regra permite concluir S ⊆ T, mas ainda não demonstra a inclusão inversa.

Voltar à explicação
Uma transformação conserva o comprimento

Com map⁡ f []=[]\operatorname{map}\ f\ []=[], map⁡ f (x:xs)=f(x):map⁡ f xs\operatorname{map}\ f\ (x:xs)=f(x):\operatorname{map}\ f\ xs e a definição usual de length, prova length⁡(map⁡ f xs)=length⁡(xs)\operatorname{length}(\operatorname{map}\ f\ xs)=\operatorname{length}(xs) para listas finitas.

Pista

O caso indutivo tem a forma x:xsx:xs. A imagem de x pode mudar, mas continua a ser um elemento.

Mais uma pista

Desenrola primeiro map e depois length; a hipótese aplica-se a map⁡ f xs\operatorname{map}\ f\ xs.

Solução

Base: length⁡(map⁡ f [])=length⁡([])=0\operatorname{length}(\operatorname{map}\ f\ [])=\operatorname{length}([])=0. Passo, com a hipótese para xsxs:

length⁡(map⁡ f (x:xs))=length⁡(f(x):map⁡ f xs)=1+length⁡(map⁡ f xs)=1+length⁡(xs)=length⁡(x:xs).\begin{aligned} \operatorname{length}(\operatorname{map}\ f\ (x:xs)) &=\operatorname{length}(f(x):\operatorname{map}\ f\ xs)\\ &=1+\operatorname{length}(\operatorname{map}\ f\ xs)\\ &=1+\operatorname{length}(xs)\\ &=\operatorname{length}(x:xs). \end{aligned}

Logo a propriedade vale para todas as listas finitas e qualquer função ff.

Confere a tua resposta:

  • Verifiquei a lista vazia.
  • Fixei um elemento x e uma cauda xs arbitrários.
  • Apliquei a hipótese só à cauda e terminei pela definição de length.

Erros frequentes

Não precisas de f ser injetiva. Valores repetidos continuam a ocupar posições distintas na lista.

Voltar à explicação
Justificar uma ordenação

Considera esta ordenação por inserção, sobre listas finitas cujos elementos têm uma ordem total:

insert x [] = [x]
insert x (y:ys)
  | x <= y = x:y:ys
  | otherwise = y : insert x ys

sort [] = []
sort (x:xs) = insert x (sort xs)

Prova por indução nas listas que, para cada valor vv,

conta⁡v(insert⁡(x,ys))=conta⁡v(ys)+1v=x,\operatorname{conta}_v(\operatorname{insert}(x,ys)) =\operatorname{conta}_v(ys)+\mathbf 1_{v=x},

com conta⁡v([])=0\operatorname{conta}_v([])=0 e conta⁡v(y:ys)=1v=y+conta⁡v(ys)\operatorname{conta}_v(y:ys)=\mathbf 1_{v=y}+\operatorname{conta}_v(ys). O indicador é 1 quando a igualdade é verdadeira e 0 quando é falsa. Usa depois esse lema para provar que sort conserva todas as multiplicidades. Que outra propriedade falta para concluir a correção da ordenação?

Pista

Ordenada não significa necessariamente uma permutação da entrada.

Solução

O lema das contagens prova-se por indução em ysys, com x,vx,v arbitrários. Na base, insert x [] = [x], que tem 1v=x\mathbf 1_{v=x} ocorrências de vv.

No passo ys=y:zsys=y:zs:

  • Se x≤yx\le y, insert x (y:zs) = x:y:zs. A contagem é 1v=x+1v=y+conta⁡v(zs)\mathbf 1_{v=x}+\mathbf 1_{v=y}+\operatorname{conta}_v(zs), a contagem de ysys mais 1v=x\mathbf 1_{v=x}.
  • Se x>yx>y, o resultado é y : insert x zs. A hipótese para a cauda dá 1v=y+conta⁡v(zs)+1v=x\mathbf 1_{v=y}+\operatorname{conta}_v(zs)+\mathbf 1_{v=x}, o mesmo valor pedido.

Para sort, a base devolve [] e conserva todas as contagens. No passo, usando o lema e depois a hipótese para xs:

conta⁡v(sort⁡(x:xs))=conta⁡v(insert⁡(x,sort⁡(xs)))=conta⁡v(sort⁡(xs))+1v=x=conta⁡v(xs)+1v=x=conta⁡v(x:xs).\begin{aligned} \operatorname{conta}_v(\operatorname{sort}(x:xs)) &=\operatorname{conta}_v(\operatorname{insert}(x,\operatorname{sort}(xs)))\\ &=\operatorname{conta}_v(\operatorname{sort}(xs))+\mathbf 1_{v=x}\\ &=\operatorname{conta}_v(xs)+\mathbf 1_{v=x}\\ &=\operatorname{conta}_v(x:xs). \end{aligned}

Falta ainda provar que a saída está ordenada. Isso usa o lema de que insert conserva a ordenação quando a lista recebida está ordenada. Conservar contagens não distingue sort da função identidade, que também conserva todos os elementos mas pode deixar a lista desordenada.

Confere a tua resposta:

  • Exigi que insert preserve ordenação quando recebe uma lista ordenada.
  • Provei o lema das contagens nos dois ramos de insert.
  • Usei o lema na prova das contagens de sort e distingui-a da ordenação.

Erros frequentes

Provar apenas ordenação aceita programas que apagam elementos ou duplicam valores. A correção exige também conservação das multiplicidades.

Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.