Princípios de Desenvolvimento de Algoritmos MAC122

Documentos relacionados
Princípios de Desenvolvimento de Algoritmos MAC122

Princípios de Desenvolvimento de Algoritmos MAC122 Prof. Dr. Paulo Miranda IME-USP

Programação Estruturada

SCC0214 Projeto de Algoritmos

Princípios de Desenvolvimento de Algoritmos MAC122 Prof. Dr. Paulo Miranda IME-USP

Algoritmos e Estruturas de Dados I Linguagem C

Estruturas de Dados, Análise de Algoritmos e Complexidade Estrutural. Carlos Alberto Alonso Sanches

Análise de Complexidade para algoritmos iterativos e recursivos

SCC Capítulo 2 Recursão

Recursão. feita uma ou mais chamadas a ela mesma.

Análise de Algoritmos Parte 4

SCC0601 Projeto de Algoritmos. Recursão

Bruno Hott Algoritmos e Estruturas de Dados I DECSI UFOP. Recursividade

ALGORITMOS AVANÇADOS UNIDADE II Recursividade. Luiz Leão

Estruturas de Dados. Revisão de Funções e Recursão. Agradecimentos

Funções recursivas. O exemplo mais comum: int fat(int n){ if(n == 0) return 1; return n*fat(n-1); }

Análise de Problemas Recursivos. Algoritmos e Estruturas de Dados Flavio Figueiredo (

Recursão David Déharbe

INF 1007 Programação II

Revisão: Tipo Abstrato de Dados Recursividade

Recursividade. Recursividade

Módulo 3 Controle de Fluxo

Algoritmos e Estruturas de Dados I

MC102 Aula 26. Instituto de Computação Unicamp. 17 de Novembro de 2016

Programação. Prof Marcelo Zorzan Prof a Melissa Zanatta

Recursividade. David Menotti Algoritmos e Estruturas de Dados II DInf UFPR

Estruturas de Dados. Módulo 4 Funções. 9/8/2005 (c) Dept. Informática - PUC-Rio 1

Técnicas de projeto de algoritmos: Indução

Adriano Cruz 21 de julho de Instituto de Matemática Departamento de Ciência da Computação UFRJ

Recursividade. Prof. Jesus José de Oliveira Neto

Universidade Federal de Uberlândia Faculdade de Computação. Linguagem C: funções

Processamento da Informação

ESTRUTURA DE DADOS (TCC )

SCC Introdução à Ciência de Computação II. Recursão

Linguagem C: agregados heterogêneos, arquivos binários, recursividade. Prof. Críston Algoritmos e Programação

Capacitação em Linguagem C Parte 1

Recursão. Aula 1. Liana Duenha. Faculdade de Computação Universidade Federal de Mato Grosso do Sul

Introdução à Programação. Recursão

Linguagem C: agregados heterogêneos, arquivos binários, recursividade. Prof. Críston Algoritmos e Programação

Suponha um conjunto habitacional, com várias famílias... imagina se todas elas morassem em uma única casa?

Universidade de São Paulo São Carlos Instituto de Ciências Matemáticas e de Computação. Recursão em C

Solução para os exercícios

Técnicas de projeto de algoritmos: Indução

INF1007: Programação 2. 0 Revisão. 06/08/2015 (c) Dept. de Informática - PUC-Rio 1

PROGRAMAÇÃO DE COMPUTADORES V - TCC Modulo 6 : Funções Escopo de Variáveis: Globais x Locais Aura - Erick

Permite modularidade (dividir programa em partes): Facilita a depuração (debug) e portabilidade.

Introdução à Ciência da Computação II. Recursão. Prof. Ricardo J. G. B. Campello

Suponha um conjunto habitacional, com várias famílias... imagina se todas elas morassem em uma única casa?

Recursividade. Objetivos do módulo. O que é recursividade

Linguagem C: Elementos fundamentais

Aula prática 5. Funções Recursivas

Subprogramas. Prof. Carlos Lopes

Lista 02: Pilhas & Filas

Recursão. Prof. Cristiano André da Costa. [Versão de Março de 2000] Definição

Programação de Computadores II. Cap. 4 Funções

MC102 Aula 27 Recursão II

Lista de Exercícios de Algoritmos - 04 Para cada um dos exercícios abaixo, faça um programa (em laboratório) que teste a função.

LÓGICA DE PROGRAMAÇÃO. PROFª. M.Sc. JULIANA H Q BENACCHIO

Escreva a função e_caracter que verifica se um determinado caracter é um numero ou não. Escreva um Programa de teste da função.

Introdução à Programação em C

Controle de Fluxo Utilizando C

LISTA DE EXERCÍCIOS MÊS 04

Recursão. Prof. Fabrício Olivetti de França. (com inspirações do slide do prof. Rodrigo Hausen)

Programação II RECURSÃO

Aula 21 - Algoritmos e Funções Recursivas

Estrutura de Dados Conceitos Iniciais

ALGORITMOS E ESTRUTURAS DE DADOS CES-11

INF 1010 Estruturas de Dados Avançadas

Estruturas de Dados. Módulo 11 Pilhas. 9/8/2005 (c) Dept. Informática - PUC-Rio 1

04 Recursão SCC201/501 - Introdução à Ciência de Computação II

Aula 10 Algoritmos e Funções Recursivas

Roteiro Prático Nº 13 Recursividade

Divisão e Conquista. Norton T. Roman. Apostila baseada nos trabalhos de Cid de Souza, Cândida da Silva e Delano M. Beder

Programação 1. Atribuição, operadores aritméticos, entrada de dados. Técnico em Eletrônica Semestre 5 02

Cap. 3 Entrada e Saída e Controle de Fluxo

Algoritmos de Enumeração

Programação de Computadores I Introdução ao C PROFESSORA CINTIA CAETANO

Solução de Recorrências

Aula 2 Comandos de Decisão

Algoritmos e Funções Recursivas

Macros e funções. Prof. Dr. Silvio do Lago Pereira. Departamento de Tecnologia da Informação Faculdade de Tecnologia de São Paulo

Estruturas de repetição

Algoritmos e Estruturas de Dados

Universidade Federal de Uberlândia Faculdade de Computação. Linguagem C: funções

Linguagens de Programação I

Aula 06: Análise matemática de algoritmos recursivos

Linguagem C. André Tavares da Silva.

Controlo de Execução. K&R: Capitulo 3

Análise e Complexidade de Algoritmos

Argumentos da linha de comando Exemplos Recursividade de funções Exemplos

Programação. MEAer e LEE. Bertinho Andrade da Costa. Instituto Superior Técnico. Argumentos da linha de comando Funções recursivas

Algoritmos e Estruturas de Dados

Linguagem C (estruturas condicionais)

Programação de Computadores II. Cap. 5 Vetores

Programação I Aula 15 Definições recursivas Pedro Vasconcelos DCC/FCUP

É interessante comparar algoritmos para valores grandes de n. Para valores pequenos de n, mesmo um algoritmo ineficiente não custa muito para ser

imprimir seus quadrados.

CES-11. Noções de complexidade de algoritmos. Complexidade de algoritmos. Avaliação do tempo de execução. Razão de crescimento desse tempo.

Transcrição:

Princípios de Desenvolvimento de Algoritmos MAC122 Prof. Dr. Paulo Miranda IME-USP

Definição: Uma função é dita recursiva quando dentro dela é feita uma ou mais chamadas a ela mesma. A ideia é dividir um problema original um subproblemas menores de mesma natureza (divisão) e depois combinar as soluções obtidas para gerar a solução do problema original de tamanho maior (conquista). Os subproblemas são resolvidos recursivamente do mesmo modo em função de instâncias menores, até se tornarem problemas triviais que são resolvidos de forma direta, interrompendo a recursão.

O termo recursão é equivalente ao termo indução utilizado por matemáticos. fat(n) = 1 se n=0 n x fat(n-1) se n>0 fibo(0) = 0 fibo(1) = 1 fibo(n) = fibo(n-1) + fibo(n-2) se n>1

#include <stdio.h> Exemplo: Calcular o fatorial de um número. Solução não recursiva float fatorial(int n){ float fat = 1.0; while(n>1){ fat *= n; n--; return fat; int main(){ float fat; fat = fatorial(6); printf("fatorial: %f\n",fat); return 0;

#include <stdio.h> Exemplo: Calcular o fatorial de um número. Solução recursiva: n! = n.(n-1)! float fatorial(int n){ if(n==0) /* Caso trivial */ return 1.0; /* Solução direta */ return n*fatorial(n-1); /* Chamada recursiva */ int main(){ float fat; fat = fatorial(6); printf("fatorial: %f\n",fat); return 0;

Cálculo da série de Fibonacci.

Cálculo da série de Fibonacci. /* Versão recursiva: */ int fibo_rec(int n){ if(n<=1) return n; else return (fibo_rec(n-1) + fibo_rec(n-2)); /* Versão não recursiva: */ int fibo(int n){ int f0,f1,f2,k; f0 = 0; f1 = 1; for(k=1; k<=n; k++){ f2 = f0+f1; f0 = f1; f1 = f2; return f0;

Árvore de Recorrência A solução recursiva é ineficiente, pois recalcula várias vezes a solução para valores intermediários: para F(6)=fibo(6) = 8, temos 2xF(4), 3xF(3), 5xF(2). F(6) F(4) + F(5) F(2) + F(3) F(3) + F(4) 0 + 1 1 + F(2) 1 + F(2) F(2) + F(3) 0 + 1 0 + 1 0 + 1 1 + F(2) 0 + 1

Exemplo: Calcular x elevado a n positivo. Solução não recursiva #include <stdio.h> float potencia(float x, int n){ float pot=1.0; while(n>0){ pot *= x; n--; return pot;

Exemplo: Calcular x elevado a n positivo. Solução recursiva: x^n = x. x^(n-1) #include <stdio.h> float potencia(float x, int n){ if(n==0) /* Caso trivial */ return 1.0; /* Solução direta */ else return x*potencia(x, n-1); /*Chamada recursiva*/

Exemplo: Calcular x elevado a n positivo. Solução recursiva: x^n = x^(n/2). x^(n/2) = (x^(n/2))^2 #include <stdio.h> /* Função recursiva mais eficiente */ float potencia(float x, int n){ float pot; if(n==0) return 1.0; /* Caso trivial */ if(n%2==0){ /* Se n é par... */ pot = potencia(x, n/2); return pot*pot; else{ /* Se n é ímpar... */ pot = potencia(x, n/2); return pot*pot*x;

Exemplo: Encontrar maior elemento de um vetor. Solução recursiva #include <stdio.h> int maiorinteiro(int v[], int n){ int m; if(n==1) return v[0]; /* Caso trivial */ else{ m = maiorinteiro(v, n-1); if(m>v[n-1]) return m; else return v[n-1]; int main(){ int max,v[5]={8,1,9,4,2; max = maiorinteiro(v, 5); printf("max: %d\n",max); return 0;

Exemplo: Imprimir elementos de um vetor. Solução não recursiva #include <stdio.h> void printvetor(int v[], int n){ int i; for(i=0; i<n; i++) printf("%d ",v[i]); Solução recursiva #include <stdio.h> void printvetor(int v[], int n){ if(n>1) printvetor(v, n-1); printf("%d ",v[n-1]);

Exemplo: Torre de Hanoi São dados um conjunto de N discos com diferentes tamanhos e três bases A, B e C. O problema consiste em imprimir os passos necessários para transferir os discos da base A para a base B, usando a base C como auxiliar, nunca colocando discos maiores sobre menores. A B C

Exemplo: Torre de Hanoi 1 passo: Mover de A para B. A B C

Exemplo: Torre de Hanoi 2 passo: Mover de A para C. A B C

Exemplo: Torre de Hanoi 3 passo: Mover de B para C. A B C

Exemplo: Torre de Hanoi 4 passo: Mover de A para B. A B C

Exemplo: Torre de Hanoi 5 passo: Mover de C para A. A B C

Exemplo: Torre de Hanoi 6 passo: Mover de C para B. A B C

Exemplo: Torre de Hanoi 7 passo: Mover de A para B. A B C

Exemplo: Torre de Hanoi #include <stdio.h> void hanoi(char orig, char dest, char aux, int n){ if(n == 1) printf("move de %c para %c.\n", orig, dest); else{ hanoi(orig, aux, dest, n-1); printf("move de %c para %c.\n", orig, dest); hanoi(aux, dest, orig, n-1); int main(){ int n; printf("número de discos: "); scanf("%d",&n); hanoi('a', 'B', 'C', n); return 0;

Torres de Hanoi. Exemplo /* Versão recursiva mais compacta: */ void hanoi(char orig, char dest, char aux, int n){ if(n>0){ hanoi(orig,aux,dest,n-1); printf("mova de %c para %c\n",orig,dest); hanoi(aux,dest,orig,n-1); int main(){ hanoi('a','b','c',3); return 0;

Torres de Hanoi. Exemplo O número número de de movimentos movimentos /* Versão recursiva mais compacta: necessários necessários */ para para mover mover n n n discos discos é é 2 2 - - 1. 1. void hanoi(char orig, char dest, char aux, int n){ if(n>0){ hanoi(orig,aux,dest,n-1); printf("mova de %c para %c\n",orig,dest); hanoi(aux,dest,orig,n-1); int main(){ hanoi('a','b','c',3); return 0;

Existem três tipos: Tipos de Recursão 1) Recursividade direta: A função tem uma chamada explícita a si própria. 2) Recursividade indireta (ou mútua): Duas ou mais rotinas dependem mutuamente uma da outra (ex: função A chama a função B, que por sua vez chama a função A). 3) Recursividade em cauda (tail): A chamada recursiva é a última instrução a ser executada (não existem operações pendentes).

Recursão em cauda Cálculo da função fatorial. /* Fatorial com recursão tradicional: */ int fatorial_rec(int n){ if(n==0) /* Caso trivial */ return 1; /* Solução direta */ else /* n! = n.(n-1)! */ return (n*fatorial_rec(n-1)); /* Fatorial com recursão em cauda: */ /* ac é um acumulador. */ int fatorial_caudaaux(int n, int ac) { if(n==0) return ac; return fatorial_caudaaux(n-1, n*ac); int fatorial_cauda(int n){ return fatorial_caudaaux(n, 1);

Recursão em cauda Cálculo da função fatorial. /* Fatorial com int int recursão fatorial_caudaaux(int fatorial_caudaaux(int tradicional: */ n, n, int int ac){ ac){ inicio: inicio: int fatorial_rec(int if(n==0) if(n==0) n){ return return ac; ac; if(n==0) else{ //Caso else{ trivial return 1; //Solução ac ac *= *= n; n; direta else // n! = n--; n--; n.(n-1)! return (n*fatorial_rec(n-1)); goto goto inicio; inicio; /* Fatorial com recursão em cauda: */ /* ac é um acumulador. */ int fatorial_caudaaux(int n, int ac) { if(n==0) return ac; return fatorial_caudaaux(n-1, n*ac); int fatorial_cauda(int n){ return fatorial_caudaaux(n, 1);

Pilha explícita Sempre é possível eliminar o uso da recursão e substituí-la pelo uso de uma pilha explícita. As funções obtidas ficam mais complexas, porém, em geral, são mais eficientes. Evita chamadas e retornos, Evita criação de registros de ativação, Permitem código mais customizado.

Pilha explícita Torres de Hanoi. /* Versão recursiva mais compacta: */ void hanoi(char orig, char dest, char aux, int n){ if(n>0){ hanoi(orig,aux,dest,n-1); printf("mova de %c para %c\n",orig,dest); hanoi(aux,dest,orig,n-1);

Pilha explícita Torres de Hanoi com pilha explícita. Definição da estrutura de pilha utilizada. typedef enum chamadas {chamada1,chamada2 chamada; typedef struct _elempilha{ chamada ch; char orig; char dest; char aux; int n; ElemPilha; typedef enum acoes {entrada,retorno,saida acao;

Pilha explícita void TorresDeHanoi_pilha(char orig, char dest, char aux, int n){ Pilha p = CriaPilha(); acao a = entrada; ElemPilha S; char tmp; do{ switch(a){ case entrada: if(n==0){ a = retorno; else{ break; case retorno: if(pilhavazia(p)) a = saida; else{ S = Desempilha(p); orig = S.orig; dest = S.dest; aux = S.aux; n = S.n; switch(s.ch){ case chamada1: break; case chamada2: break; break; case saida: break; while(a!=saida); LiberaPilha(p);

void TorresDeHanoi_pilha(char orig, S.ch = chamada1; else{ char dest, S.ch = chamada1; S.orig = orig; S = Desempilha(p); char aux, S.orig = orig; S.dest = dest; orig = S.orig; int n){ S.dest = dest; S.aux = aux; dest = S.dest; Pilha p = CriaPilha(); S.aux = aux; S.n = n; aux = S.aux; acao a = entrada; S.n = n; Empilha(p, S); n = S.n; ElemPilha S; Empilha(p, S); switch(s.ch){ char tmp; /* TorresHanoi(orig,aux,dest,n-1);*/ case chamada1: /* TorresHanoi(orig,aux,dest,n-1);*/ tmp = dest; do{ tmp = dest; dest = aux; break; switch(a){ dest = aux; aux = tmp; case chamada2: case entrada: aux = tmp; n--; if(n==0){ n--; break; a = retorno; else{ break; case retorno: if(pilhavazia(p)) a = saida; Pilha explícita break; case saida: break; while(a!=saida); LiberaPilha(p);

Pilha explícita void TorresDeHanoi_pilha(char orig, char dest, char aux, int n){ Pilha p = CriaPilha(); acao a = entrada; ElemPilha S; char tmp; else{ S = Desempilha(p); orig = S.orig; dest = S.dest; aux = S.aux; n = S.n; switch(s.ch){ case chamada1: do{ switch(a){ case entrada: if(n==0){ a = retorno; tmp = orig; tmp else{ = orig; orig = aux; orig = aux; aux = tmp; aux = tmp; n--; n--; a = break; entrada; a case = entrada; retorno: if(pilhavazia(p)) a = saida; break; case chamada2: printf("mova de %c para %c\n",orig,dest); break; printf("mova de %c para %c\n",orig,dest); /* TorresHanoi(aux,dest,orig,n-1); */ /* TorresHanoi(aux,dest,orig,n-1); */ break; case saida: break; while(a!=saida); LiberaPilha(p);

Pilha explícita void TorresDeHanoi_pilha(char orig, char dest, char aux, int n){ Pilha p = CriaPilha(); acao a = entrada; ElemPilha S; char tmp; else{ S = Desempilha(p); orig = S.orig; dest = S.dest; aux = S.aux; n = S.n; switch(s.ch){ case chamada1: do{ switch(a){ case entrada: if(n==0){ a = retorno; else{ break; case chamada2: break; /* não existem operações pendentes.*/ /* não existem operações pendentes.*/ break; case saida: break; break; case retorno: while(a!=saida); if(pilhavazia(p)) LiberaPilha(p); a = saida;

Recursão indireta Duas ou mais rotinas dependem mutuamente uma da outra (ex: função f chama a função g, que por sua vez chama a função f). f(n) = 0 se n = 0 g(n-1) se n > 0 g(n) = 1 se n = 0 f(n-1) se n > 0

Recursão indireta Transformação da notação infixa para pós-fixa. int main(){ char expr[]="a*(b+c)*(d-g)*h"; In2Pos(expr); return 0;

Recursão indireta Transformação da notação infixa para pós-fixa. void Erro(){ printf("erro\n"); exit(-1); void In2Pos(char expr[]){ int i; i = 0; Expressao(expr, &i); printf("\n"); if(expr[i]!='\0') Erro();

Recursão indireta Transformação da notação infixa para pós-fixa. void Expressao(char expr[], int *i){ char op; bool fim=false; Termo(expr, i); do{ op = expr[*i]; if(op=='+' op=='-'){ (*i)++; Termo(expr, i); printf("%c",op); else fim = true; while(!fim);

Recursão indireta Transformação da notação infixa para pós-fixa. void Termo(char expr[], int *i){ char op; bool fim=false; Fator(expr, i); do{ op = expr[*i]; if(op=='*' op=='/'){ (*i)++; Fator(expr, i); printf("%c",op); else fim = true; while(!fim);

Recursão indireta Transformação da notação infixa para pós-fixa. void Fator(char expr[], int *i){ char c; c = expr[*i]; if(c>='a' && c<='z'){ printf("%c",c); (*i)++; else if(c=='('){ (*i)++; Expressao(expr, i); if(expr[*i]==')') (*i)++; else Erro(); else Erro();