• Nenhum resultado encontrado

Programação II RECURSÃO

N/A
N/A
Protected

Academic year: 2021

Share "Programação II RECURSÃO"

Copied!
29
0
0

Texto

(1)

Programação II

RECURSÃO

Bruno Feijó

(2)

Motivação

2

Escher: Metamorphosis (1937) - Drawing Hands (1948) – Relativity (1953)

http://www.worldofescher.com/gallery/

Alguém diz: “

Esta sentença é falsa

!”

Paradoxo do Cretense Mentiroso

um homem natural de Creta, em praça pública,

anuncia: “

Todo cretense é mentiroso

!”

Estas sentenças são Verdadeiras ou Falsas ?

O que há de comum neste slide ?

(3)

Definições Recursivas

• Em uma definição recursiva um item é definido em termos

de si mesmo, ou seja, o item que está sendo definido

(4)

Conceito de Recursão

4

• Em uma definição recursiva um item é definido em termos de si mesmo, ou seja,o item que está sendo definido aparece como parte da definição;

• Mas, atenção: a definição f(x) f(x) não revela nada sobre a função f.

• A definição recursiva da função fatorial fat(n) = n(n  1)    1 é a seguinte:

• Uma definição recursiva é definida por um ou mais casos base e por um ou mais passos indutivos envolvendo a chamada da própria função (denominada “chamada recursiva”).

• O caso base é uma situação trivial da função, onde calcular o valor da função é imediato, direto e trivial.

• O passo indutivo é a aplicação recursiva da função em uma versão de menor porte do problema. Imagine o passo indutivo como sendo um degrau, situado logo abaixo do nível do problema proposto originalmente, em direção a um caso base. A maior dificuldade neste método de resolver problemas é termos confiança na solidez desse degrau ! Isto é:

confiança na validade de adotarmos a Hipótese Indutiva. que corresponde a supor a versão de menor porte do problema como estando completamente resolvida. Isto é um “método indutivo para resolver problemas”.

• O termo “indutivo” é usado para salientar que a solução em um passo é induzida pela

solução do passo anterior. A relação entre recursão e indução é apresentada mais a frente.

0

),

1

(

0

,

1

)

(

n

se

n

fat

n

n

se

n

fat

Caso Base

Recorrência (ou Passo Indutivo)

Chamada Recursiva (ou Hipótese Indutiva)

(5)

O processo da Recursão

5

• A definição recursiva é uma maneira de especificar a solução de uma instância de um problema de tamanho n em termos de soluções para instâncias menores.

• Neste processo, cria-se uma pilha de operações pendentes (uma expansão) que são resolvidas quando se encontra o caso base (uma contração). Por exemplo:

Problema tamanho n

fat(3) = 3  fat(2) fat(2) = 3  fat(1)

fat(1) = 2  fat(0)

fat(0) = 1 Caso base

expansão contração 3 * fat(2) 3 * (2 * fat(1)) 3 * (2 * (1 * fat(0))) 3 * (2 * (1 * 1)) Caso Base 3 * (2 * 1) 3 * 2 6 fat(3)

(6)

Implementação de uma Definição Recursiva

6

• Seja a definição recursiva de fatorial:

• Uma vez que encontramos a definição recursiva de uma função, a implementação é direta, simples, um trabalho automático:

• Você pode verificar se a sua definição e a sua implementação estão corretas acompanhando a execução de um exemplo:

• Este acompanhamento chama-se TRAÇO. Não dependa de fazer o traço para encontrar a definição recursiva de uma função. Use o traço para verificar e não para criar a solução. • Ademais, somente funções matemáticas admitem o TRAÇO. Traço não faz sentido para

procedimentos (e.g. funções em C que disparam ações e/ou não retornam valor). Nestes casos, acompanham-se as variáveis do procedimento. As variáveis definem o estado do mundo. … e nem sempre é fácil acompanhar o estado do mundo!

fat(0) = 1

fat(n) = n

fat(n

1)

int fat (int n) { if (n==0) return 1; else return n*fat(n-1); }

(7)

Outro Exemplo de Definição Recursiva

7

• A definição recursiva da soma da série de números naturais, g(n) =1

2

n

, é:

• Este exemplo é mais interessante que o do fatorial, porque há uma bela forma fechada que representa esta soma:

• Nem toda a definição recursiva tem uma bela forma fechada (… raras têm!). Aliás, quando tem, não faz sentido implementar a função recursivamente no computador.

• A implementação recursiva de g(n) também é imediata:

• Note que não existem construtores de loop (e.g. for ou while) na implementação de definições recursivas! Há famílias de linguagens que não têm estes construtores (for e

while), porque usam recursão (e.g. linguagens funcionais, como Lisp, e linguagens de programação em lógica, como Prolog).

g(1) = 1

g(n) = g(n

1) + n

1

2

n = n(n

1)/2

int g(int n) { if (n == 1) return 1; else return n + g(n - 1); }

(8)

Recursão e Indução (Material Avançado)

8

• Existe uma estreita ligação entre indução e definições recursivas. Indução é talvez a forma mais natural de raciocinar sobre processos recursivos.

• Indução é um método para provar que uma afirmação S(n) é válida para todo n: • Primeiro mostre que S(1) (ou S(0)) é valida

• Depois assuma que S(k) é válida (esta é a hipótese indutiva) • Mostre que S(k+1) é válida

• Então, S(n) é válida para todo n.

• Um exemplo clássico: provar que

1

2

n = n(n

1)/2

para todo n • S(1) = 1(1  1)/2 = 1, i.e. S(1) é válida!

• Hipótese:

1

2

k = k(k

1)/2

é uma afirmação válida

• Demonstração que S(k+1) é válida, i.e.

1

2

k

(k

1)= (k

1)(k

2)/2

• Pela hipótese, o lado esquerdo é k(k

1)/2

(k

1)

• Colocando

(k

1)

em evidência, tem-se:

(k

1)(k/2 +1)

, i.e.

(k

1)(k

2)/2

• Indução cai naturalmente no mesmo paradigma da definição recursiva. São duas variantes do mesmo tema.

• A forma recursiva para o exemplo acima já vimos que é:

• Nem toda a definição recursiva tem uma bela forma fechada (como a n(n

1)/2

)

g(1) = 1

g(n) = g(n

1) + n

Este assunto não cai em provas. São informações que ajudam a entender recursão.

(9)

Um Roteiro para Encontrar Soluções Recursivas

9

• Devemos observar que, no método indutivo, o problema vai sucessivamente caindo em problemas de menor porte, até que o caso base é alcançado.

• O passo indutivo corresponde a descobrirmos como modificar o valor vindo da chamada recursiva para ter o resultado final procurado. E o que devemos fazer é sempre muito simples, como uma espécie de retoque final (pois o maior trabalho já foi feito pela hipótese indutiva, ou seja: pela chamada recursiva)

• Não começe tentando simular a execução (deixe isto como teste após encontrar solução. • Como um roteiro faça sempre o seguinte (até ficar automático no seu cérebro):

1. Defina o domínio e a imagem da função (lembre que são conjuntos: conjunto dos naturais, conjunto de todos os strings, ...). E entenda o que a função faz (escreva exemplos). Saber o que a função faz, ajuda muito a encontrar a chamada recursiva!!! 2. Verifique se há precondições (e.g. algum valor não permitido, ou se é simplesmente a

condição de pertencer ao domínio, ou ...).

3. Identifique os elementos mais neutros, simples, do domínio. Os casos base geralmente (mas não necessariamente) dizem respeito a eles.

4. Formule os casos base

5. Encontre as chamadas recursivas (um degrau a menos na direção do caso base). A chamada recursiva resolve a maior parte do problema e retorna um valor concreto. 6. Encontre os passos indutivos como sendo um retoque que você faz com o valor

recebido da chamada recursiva. Se você começar a complicar, é sinal de que está errado !!

Com a prática, junte 5 e 6. Depois de formular a solução conceitual, pense em como funcionará na máquina, … esboçando o código!

(10)

Problema de Ordem-n:

Precondição: não há situações especiais

Caso(s) Base:

Hipótese Indutiva (Chamada Recursiva):

Passo(s) Indutivo(s):

naturais (inteiros não-negativos)

inteiros (neg. e não-negativos).

R reais

S conjunto das strings

fat(n) :

  

n

 

n=0

fat(n) = 1

fat(n-1)

fat(n) = n * fat(n-1)

Usando o roteiro do slide anterior, primeiro escreva um esboço de solução da maneira mais independente possível da linguagem de computador em questão. Vamos chamar esta solução de Solução Conceitual. Por exemplo:

Depois escreva o Código!

E, por fim, tente seguir a execução do Código.

(11)

EXEMPLOS

(12)

Tamanho e Cópia de Strings

int main(void) { ... printf("----\ntamanho= %d\n",stringLen(a)); stringCopy(n,"ana"); printf("n=%s\n",n); stringCopy(n,"oi"); printf("n=%s\n",n); printf("n[2]=%d n[3]=%d\n\n",n[2],n[3]); return 0; } n=oi ----tamanho= 3 n=ana n[2]=0 n[3]=0

valor numérico de \0 é 0 (zero). Imprimir \0 como %c dá branco

Escreva as funções stringLen e stringCopy recursivamente.

Não veja as respostas nos slides seguintes. Tente resolver!

(13)

Exemplo: tamanho de um string

13 int tamanho(char * s) { if (*s == '\0') return 0; else return 1 + tamanho(s+1); } Solução conceitual: tamanho(s): S  N

se s é vazia então tamanho é 0 caso contrário

tamanho(s) = 1 + tamanho do resto da string

int tamanho(char * s) // versao com sintaxe [] de indices { if (s[0] == '\0') return 0; else return 1 + tamanho(&s[1]); }

(14)

Exemplo imprimir string

14

void imprimeStr(char * s) // versao ponteiro { if (*s) // ou: if (*s != '\0') { printf("%c",*s); imprimeStr(s+1); } } #include <stdio.h> void imprimeStr(char *); int main(void) {

char s[] = "Bom Dia"; imprimeStr(s); printf("\n"); return 0; } /* versao vetor void imprimeStr(char * s) { if (s[0]) // ou: if (s[0] != '\0') { printf("%c",s[0]); imprimeStr(&s[1]); } } */ Solução conceitual imprime(s)

se s é vazia então nada imprime caso contrário

imprime 1 caractere de s e depois imprime resto da string

Imprimir string invertido:

void imprimeInv(char * s) { if (*s) { imprimeInv(s+1); printf("%c",*s); }

(15)

Exemplo stringCopy

15

void stringCopy(char * s, char * t) { *s = *t; if (*t) stringCopy(s+1,t+1); } #include <stdio.h>

void stringCopy(char *, char *); int main(void)

{

char s[] = "Bom Dia";

char * a = (char *)malloc(51); stringCopy(a,s);

return 0; }

Solução conceitual

stringCopy(s,t) copia t para s

se t é vazia, então s é vazia caso contrário

copia 1caractere de t para s e

copia restante da string t para a próxima posição de s

void stringCopy(char * s, char * t) {

if (*s = *t)

stringCopy(s+1,t+1); }

void stringCopy(char * s, char * t) { if (*t == '\0') *s == '\0‘; else { *s = *t; stringCopy(s+1,t+1); } }

void stringCopy(char * s, char * t) { s[0] = t[0]; if (t[0]) // ou if (t[0] != '\0’) stringCopy(&s[1],&t[1]); } ou de maneira mais concisa

(16)

16

Exemplo: Potenciação

• Exemplo: função recursiva para cálculo de potenciação

/* Função recursiva para cálculo de potenciacao */

int pot (int x, int n)

{

if (n==0)

return 1;

else

return x*pot(x,n-1);

}

Caso BASE

Passo

Recursivo

(17)

17

Exemplo com 2 Casos Base e 2 Chamadas Recursivas

• Série de Fibonacci (um termo é a soma dos dois anteriores): 0, 1, 1, 2, 3, 5, 8, … • Definição recursiva da Série de Fibonacci:

• Implementação:

fib(0) = 0

fib(1) = 1

fib(n) = fib(n

1) + fib(n

2) se n

1

int fib (int n) { if (n==0) return 0; else if (n==1) return 1; else

return (fib(n-1) + fib(n-2)); }

(18)

ILUSTRAÇÃO DA RECURSÃO EM C

(19)

19

Funções Recursivas

• Tipos de recursão:

– direta:

• uma função A chama a ela própria

– indireta:

• uma função A chama uma função B que, por sua vez, chama A

• Comportamento:

– quando uma função é chamada recursivamente,

cria-se um ambiente local para cada chamada

– as variáveis locais de chamadas recursivas são independentes

entre si, como se estivéssemos chamando funções diferentes

(20)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

-3

-3

n

r

main

n

f

fat(3)

(21)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

-2

-3

-3

n

r

main

n

f

fat(3)

n

f

fat(2)

(22)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

-1

-2

-3

-3

n

r

main

n

f

fat(3)

n

f

fat(2)

n

f

fat(1)

(23)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

-0

-1

-2

-3

-3

n

r

main

n

f

fat(3)

n

f

fat(2)

n

f

fat(1)

n

f

fat(0)

(24)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

1

0

-1

-2

-3

-3

n

r

main

n

f

fat(3)

n

f

fat(2)

n

f

fat(1)

n

f

fat(0)

(25)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

1

1

-2

-3

-3

n

r

main

n

f

fat(3)

n

f

fat(2)

n

f

fat(1)

(26)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

2

2

-3

-3

n

r

main

n

f

fat(3)

n

f

fat(2)

(27)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

6

3

-3

n

r

main

n

f

fat(3)

(28)

08/03/10 (c) Casanova, Gattass, Viterbo

Funções Recursivas

#include <stdio.h>

int fat (int n);

int main (void)

{ int n = 3;

int r;

r = fat ( n );

printf("Fatorial de %d = %d \n", n, r);

return 0;

}

/* Função recursiva para cálculo do fatorial */

int fat (int n)

{

int f;

if (n==0)

f=1;

else

f= n*fat(n-1);

return f;

}

6

3

n

r

main

(29)

29

Referências

Waldemar Celes, Renato Cerqueira, José Lucas Rangel,

Introdução a Estruturas de Dados, Editora Campus

(2004)

Capítulo 4 – Funções (pag. 54)

Referências

Documentos relacionados

Foram incluídos pa- cientes de ambos os gêneros, com ida- de ≥18 anos, que utilizaram ventilação mecânica não invasiva devido ao quadro de insuficiência respiratória secundária

A prova do ENADE/2011, aplicada aos estudantes da Área de Tecnologia em Redes de Computadores, com duração total de 4 horas, apresentou questões discursivas e de múltipla

17 CORTE IDH. Caso Castañeda Gutman vs.. restrição ao lançamento de uma candidatura a cargo político pode demandar o enfrentamento de temas de ordem histórica, social e política

O enfermeiro, como integrante da equipe multidisciplinar em saúde, possui respaldo ético legal e técnico cientifico para atuar junto ao paciente portador de feridas, da avaliação

o34eto es'ec-9ico de estdo, elas ser/o reto0adas na discss/o do contedo da Didática descrito 'or 'ro9essores

Equipamentos de emergência imediatamente acessíveis, com instruções de utilização. Assegurar-se que os lava- olhos e os chuveiros de segurança estejam próximos ao local de

Tal será possível através do fornecimento de evidências de que a relação entre educação inclusiva e inclusão social é pertinente para a qualidade dos recursos de

Mário Jabur Filho, conforme dispõe a legislação vigente, comunica que estarão abertas as inscrições para seleção dos candidatos para preenchimento de vaga para Médico