Algoritmos e Estruturas de Dados I – Recursão

Slides:



Advertisements
Apresentações semelhantes
Python: Recursão Claudio Esperança.
Advertisements

Aula T-09 – BCC202 Listas (Parte 1) Túlio Toffolo www. decom. ufop
Estruturas de Dados Marcio Gonçalves.
Solved Exercises 1. Finding the Peak. Let A= a1,…,an be a sequence of n numbers with the following property: there is p in {1,…,n} for which (i) the.
Universidade Federal de Campina Grande – UFCG Centro de Engenharia Elétrica e Informática – CEEI Departamento de Sistemas e Computação – DSC Máquina de.
Recursividade Prof. Rosana Palazon.
Recursividade Prof. Alex F. V. Machado
Algoritmos e Estrutura de Dados I
Análise Projeto de Algoritmos
1 Complexidade de Algoritmos Complexidade de pior caso Complexidade de melhor caso de uso bem menos freqüente em algumas situações específicas Complexidade.
1 MergeSort Seja uma lista A de n elementos. O algoritmo consiste das seguintes fases Dividir A em 2 sub-listas de tamanho n/2 Conquistar: ordenar cada.
David Menotti Algoritmos e Estruturas de Dados I DECOM – UFOP
Recursividade Conceitos e Aplicações.
Ordenação Facilitar e aumentar a eficiência das operações de pesquisa sobre esses dados Pode ser crescente ou decrescente A seqüência de entrada, normalmente,
THOBER CORADI DETOFENO, MSC. Aula 07
Classificação de dados por Intercalação: MergeSort
Dividir-e-Conquistar
Universidade Federal do Espírito Santo
Informática Teórica Engenharia da Computação
Algoritmos e Estruturas de Dados I – Estruturas de Dados
Algoritmos e Estruturas de Dados I – Estruturas de Controle de Fluxo
Aux a fim início Simulação da execução de um algoritmo que troca o conteúdo de duas variáveis recebidas e apresenta o resultado da inversão: leia a,b a.
Aula prática 8 Ponteiros Monitoria de Introdução à Programação
Introdução à Programação
Linguagem de programação I A Carlos Oberdan Rolim Ciência da Computação Sistemas de Informação.
Recursão.
Listas, Filas e Pilhas Katia Guimarães.
Aula 3 Listas, pilhas, filas.
Estruturas de Dados Aula 9: Listas (parte 1)
Algoritmos e Estruturas de Dados I – Recursão
Estruturas de Dados Aula 14: Recursão 04/06/2014.
Procedimentos e Funções
Algoritmos e Estruturas de Dados I – Ponteiros
Algoritmos e Estruturas de Dados I – Recursão
Recursão Uma função é dita recursiva quando dentro do seu código existe uma chamada para si mesma Exemplo Cálculo do fatorial de um número:
Universidade do Vale do Rio dos Sinos - São Leopoldo -
Algoritmos - Profa. Alciléia1 Profa. Maria Alciléia Alves Rocha Funções em C 2011.
Algoritmos e Estruturas de Dados RECURSIVIDADE. O que é recursividade? Recursividade significa algo ser definido em termos de sí próprio. Em termos de.
Algoritmos e Estruturas de Dados I – Estruturas de Dados
Algoritmos e Estruturas de Dados I – Estruturas de Controle de Fluxo
Algoritmos e Estruturas de Dados I – Modularização
Algoritmos e Estruturas de Dados I – Registros
Recursividade Bruno Silva.
Como analisar um algoritmo
Algoritmos e Estruturas de Dados I – Estruturas de Dados
Programação Dinâmica.
Fundamentos de linguagens de programação
Recursividade Profs. De Prog2 e Lab2.
Algoritmos e Estruturas de Dados I – Estruturas de Dados
Algoritmos.
Algoritmos e Estruturas de Dados I – Estruturas de Controle de Fluxo
Complexidade de Computação Katia Guimarães. Avaliando a Qualidade de um Algoritmo É preciso ter bem definido –O que é dado de entrada e –O que é esperado.
Algoritmos e Programação MC102
ATAL – Prof. Jorge Figueiredo Ordenação AT AL Análise e Técnicas de Algoritmos Análise de Algoritmos de Ordenação.
Elaine Teixeira de Oliveira
Algoritmo e Estrutura de Dados I
Linguagem de Programação I Parte IV
Professor Luiz José Hoffmann Filho
Estrutura de Dados Aula 3 - Listas
PCI- Funções e Procedimentos Profa. Mercedes Gonzales Márquez.
Métodos de Ordenação Externa
Linguagem de Programação
INE5408 Estruturas de Dados Listas Encadeadas Simples.
Aula Prática 5 Recursão Monitoria  Na linguagem C, como em muitas outras linguagens, uma função pode chamar a si própria.  Uma função assim.
Alocação Dinâmica Dilvan Moreira. Objetivos  Entender o que são e como usar:  Gerenciamento de Memória  Alocação Dinâmica em C.
Recursividade e análise Cristiano Arbex Valle Vinicius Fernandes dos Santos
Lógica de Programação Aula 06 Prof. Marcelo Marcony.
Programação de Computadores I – Recursão Profa. Mercedes Gonzales Márquez.
Programação de Computadores I – Recursão
Transcrição da apresentação:

Algoritmos e Estruturas de Dados I – Recursão Profa. Mercedes Gonzales Márquez

Recursão A solução de um problema é dita recursiva quando ela é escrita em função de si própria para instancias menores do problema. Veja, por exemplo, o problema de calcular o fatorial de um número n. A solução iterativa pode ser facilmente implementado assim: Função inteiro Fatorial (inteiro: n) inteiro: i; Início result ← 1; para i de 2 até n repita result ← result * i; Fim para Retorne(result) Implementação não Recursiva

Recursão A solução recursiva está diretamente ligada ao conceito de indução matemática, no qual usa-se como hipótese que a solução de um problema de tamanho n pode ser obtida a partir da sua solução de tamanho menor, por exemplo, n-1. No caso do exemplo do fatorial teriamos: Função inteiro Fatorial (inteiro: n) inteiro: i; Início Se n=0 então fatorial ← 1; Senão fatorial ← n*fatorial(n-1) Fim se Fim Implementação Recursiva

Recursão Toda recursão é composta por: ● Caso base – Uma ou mais instâncias do problema que podem ser solucionadas facilmente (solução trivial) Chamadas Recursivas – O objeto define-se em termos de si próprio, procurando convergir para o caso base. Por exemplo, o fatorial de um número n pode ser calculado a partir do fatorial do número n−1.

Recursão Esquematicamente, os algoritmos recursivos tem a seguinte forma: Se <condição para o caso base> então resolução direta para o caso base Senão uma ou mais chamadas recursivas Fim se Sem a condição de parada, expressa no caso base, uma recursão iria se repetir indefinidamente.

Recursão Implementação Recursiva Função inteiro fatorial (inteiro: n) inteiro: i; Início Se n=0 então retorne(1) Senão retorne(n*fatorial(n-1)) Fim se Fim Implementação Recursiva

Fatorial = 4 * (Fatorial(3)) Teste de mesa para n=4 Fatorial = 4 * (Fatorial(3)) Fatorial = 1 Fatorial = 1 * (Fatorial(0)) 1 Fatorial = 1 Fatorial = 2 * (Fatorial(1)) 1 Fatorial = 2 Fatorial = 3 * (Fatorial(2)) 2 Fatorial = 6 6 Fatorial = 24

Sequência de Fibonacci – Definição Iterativa 0 1 1 2 3 5 8 13 … Simulação: Anterior = 0 atual = 1 seguinte = 1 Anterior = 1 atual = 1 seguinte = 2 Anterior = 1 atual = 2 seguinte = 3 Anterior = 2 atual = 3 seguinte = 5 Anterior = 3 atual = 5 seguinte = 8 ….

Função inteiro fib(inteiro: n) Inteiro: i, anterior, atual, seguinte Início se (n= 1) retorne (0) senão se (n = 2) retorne (1) senão anterior ← 0 atual ← 1 para i de 3 até n repita seguinte ← atual + anterior anterior ← atual atual ← seguinte fim para fim se retorne (atual) Fim

Sequência de Fibonacci – Definição Recursiva fib(1) = 0 fib(2) = 1 fib(3) = fib(2) + fib(1) fib(4) = fib(3) + fib(2) Fib(n) = fib(n-1) + fib(n - 2)

Função inteiro fib_rec(inteiro: n) Início se (n = 1) retorne (0) senão se (n = 2) retorne (1) senão retorne fib_rec(n - 1) + fib_rec(n - 2) fim se Fim

O que acontece na memória Recursão O que acontece na memória Precisamos entender como é feito o controle sobre as variáveis locais em chamadas recursivas. A memória de um sistema computacional é dividida em três partes: Espaço Estático: Contém as variáveis globais e código do programa. Heap: Para alocação dinâmica de memória. Pilha: Para execução de funções.

O que acontece na pilha: Recursão O que acontece na pilha: Toda vez que uma função é invocada, suas variáveis locais são armazenadas no topo da pilha. Quando uma função termina a sua execução, suas variáveis locais são removidas da pilha. Considere o exemplo: Função inteiro f1(inteiro: a, inteiro: b) Inteiro: c c←5 retorne (c+a+b) Função inteiro f2(inteiro: a, inteiro: b) c ← f1(b, a) retorne (c) /*Chamada */ f2(2, 3)

O que acontece na memória Recursão O que acontece na memória Quando f2(2,3) é invocada, suas variáveis locais são alocadas no topo da pilha.

O que acontece na memória Recursão O que acontece na memória A função f2 invoca a função f1(b,a) e as variáveis locais desta são alocadas no topo da pilha sobre as de f2.

O que acontece na memória Recursão O que acontece na memória A função f1 termina, devolvendo 10. As variáveis locais de f1 são removidas da pilha.

O que acontece na memória Recursão O que acontece na memória Finalmente f2 termina a sua execução devolvendo 10. Suas variáveis locais são removidas da pilha.

Recursão Exemplo 3. Soma de elementos de um vetor : Faça um algoritmo que preencha por leitura um vetor de 10 elementos inteiros, imprima o seu conteúdo, e o resultado do somatório dos seus elementos, calculado por uma função recursiva. Função inteiro soma(inteiro: n) Início se (n = 1) retorne (v[n]) senão retorne (v[n]+soma(n-1)) Fim

Recursão Exemplo 4. Escreva uma função recursiva que recebe como parâmetros um número real X e um inteiro n e retorna o valor de Xn. Obs.:n pode ser negativo. Função Real Potencia(real: X,inteiro: n) Início Se ( n ==0 ) então retorne (1) Senão se ( n <0 ) então retorne (1 / (X* Potencia(X, abs(N)-1)) senão retorne (X* Potencia(X, N -1)) Fim se Fim

Recursão Exemplo 5. Reescreva a função abaixo tornando-a recursiva. Esta função conta o número de algarismos (dígitos) que possui um número inteiro n. Função inteiro digitos(inteiro: n) Inteiro: cont Início cont ← 1; enquanto (abs(n )>9) faça n = div(n,10) cont ← cont+1 Fim enquanto retorne (cont) Fim

Recursão Função inteiro digitos(inteiro: n) Início Se (abs( n )<10) então retorne (1) Senão retorne (1+ digitos(div(n,10)) Fim se Fim

Recursão Exemplo 6. Escreva uma função recursiva que calcule a soma dos dígitos de um inteiro positivo n. A soma dos dígitos de 132, por exemplo, é 6. Função inteiro somadigitos(inteiro: n) Início Se (n <10) então retorne (n) Senão retorne (mod(n,10)+ somadigitos(div(n,10)) Fim se Fim

Recursão Exemplo 7. Escreva uma função recursiva que determine o inverso de um número inteiro positivo n, dado o número de dígitos de n. O inverso é obtido pela inversão dos dígitos do número. O inverso do 132, por exemplo, é 231.

Recursão Exemplo 7. Escreva uma função recursiva que determine o inverso de um número inteiro positivo n, dado o número de dígitos de n. O inverso é obtido pela inversão dos dígitos do número. O inverso do 132, por exemplo, é 231. Função inteiro inverso(inteiro: n, inteiro: ndig) Início Se (n <10) então retorne (n) Senão retorne (mod(n,10)*10**(ndig-1)+ inverso(div(n,10),ndig-1) Fim se Fim

Recursão Exemplo 8. Verificar se um número é capicua utilizando uma função recursiva que considere como entrada o número inteiro positivo n e o número de algarismos de n. Um número capicua é aquele número que coincide com seu inverso, de acordo a definição do exemplo 7. Função lógico capicua(inteiro: n, inteiro: ndig) Início Se (n <10) então retorne (1) Senão se (mod(n,10)<>div(n,10**(ndig-1)) retorne (0) fim se capicua((div(mod(n,10**(ndig-1))),10),ndig-2) Fim se Fim

Recursão Exemplo 9. Seja uma linguagem hipotética na qual não existem operadores para adição, nem subtração. Sabe-se que nesta linguagem, existe uma função prox(n) que dá o sucessor do número n, e existe também uma função ant(n) que dá o predecessor de um número n. Usando apenas essas funções (prox e ant), defina a função recursiva som(x,y), que toma como argumento os números naturais x e y, e retorna sua soma.

Recursão Função inteiro som(inteiro: x,y) Início Se (x=0) então retorne (y) Senão se (y=0) então retorne (x) senão retorne (som(ant(x),prox(y)) Fim se Fim

Recursão Pode-se realizar a busca de duas formas: Exemplo 10- Faça um algoritmo que realize uma busca em um vetor ordenado de elementos. Pode-se realizar a busca de duas formas: Busca Sequencial: os elementos são pesquisados fazendo a varredura completa do vetor (ineficiente). O pior caso ocorrerá quando o elemento estiver no último índice do vetor. Busca Binária

Busca Sequencial Procurar por R A C E H L M P R T V -8 Comparações! Se estivermos procurando o item V, o número de comparações seria a quantidade de elementos no vetor O ideal seria dividir o vetor pela metade para então procurar (Busca Binária)

Busca Binária Divide seu vetor em duas metades Três condições Se o item for igual ao item que está na metade do vetor, o item foi encontrado Se for menor, procure na primeira metade Se for maior procure na segunda metade

Busca Binária Procurar por R A C E H L M P R T V I F I F X X 1 2 3 4 5 6 7 8 9 10 A C E H L M P R T V I F I F X X -2 Comparações! Casos piores: quando os itens estiverem no início do vetor. Nesse caso, seria melhor utilizar busca seqüencial. Mas como saber quando o ítem está no início do vetor?

Busca Binária Procedimento Busca_Binária(inteiro: x,Inicio, Fim); Inteiro: meio Início meio ← div((inicio + fim), 2) Se fim < inicio então escreva (‘Elemento Não Encontrado’) Senão se (v[meio]) = x então escreva (‘Elemento está na posição ’,meio) senão se v[meio] < x então inicio ← meio +1; Busca_Binaria (x, inicio, fim); fim ← meio - 1; fim se Fim

Recursão Exemplo 11-Problema da Torre de Hanói O problema ou quebra-cabeça conhecido como torre de Hanói consiste em transferir, com o menor número de movimentos, a torre composta por N discos do pino A (origem) para o pino C (destino), utilizando o pino B como auxiliar. Somente um disco pode ser movimentado de cada vez e um disco não pode ser colocado sobre outro disco de menor diâmetro.

Recursão Solução: Transferir a torre com N-1 discos de A para B, mover o maior disco de A para C e transferir a torre com N-1 de B para C. Embora não seja possível transferir a torre com N-1 de uma só vez, o problema torna-se mais simples: mover um disco e transferir duas torres com N-2 discos. Assim, cada transferência de torre implica em mover um disco e transferir de duas torres com um disco a menos e isso deve ser feito até que torre consista de um único disco.

Recursão procedimento MoveTorre(inteiro:n, literal: Orig, Dest, Aux) início se n = 1 então MoveDisco(Orig, Dest) senão MoveTorre(n - 1, Orig, Aux, Dest) MoveDisco(Orig, Dest) MoveTorre(n - 1, Aux, Dest, Orig) fim se fim procedimento MoveDisco(literal:Orig, Dest) início Escreva(“Movimento: ”, Orig, “ -> ”, Dest) fim

Recursão Uma chamada a MoveTorre(3, ‘A’, ‘C’, ‘B’) teria a seguinte saída: Movimento: A -> C Movimento: A -> B Movimento: C -> B Movimento: A -> C Movimento: B -> A Movimento: B -> C Movimento: A -> C

Recursão Exemplo 12- A recursividade pode ser utilizada para gerar todas as possíveis permutações de um conjunto de símbolos. Por exemplo, existem seis permutações no conjunto de símbolos A, B e C: ABC, ACB, BAC, BCA, CBA e CAB. O conjunto de permutações de N símbolos é gerado tomando-se cada símbolo por vez e prefixando-o a todas as permutações que resultam dos (N - 1) símbolos restantes. Consequentemente, permutações num conjunto de símbolos podem ser especificadas em termos de permutações num conjunto menor de símbolos. Formule um algoritmo recursivo para este problema.

Recursão Solução: O número total de permutações de n objetos é dado por P(n) = n!. Ou seja: para 3 objetos P(3) = 3! = 3 x 2 x 1 = 6 permutações. Para 4 objetos temos: P(4) = 4! = 4 x 3 x 2 x 1 = 24 permutações. O recurso básico para a elaboração do algoritmo consiste em montar uma árvore de recursão para uma “instância” de solução para o problema, por exemplo 3 objetos.

Recursão procedimento PermutaRecursiva(literal: str[], inteiro: k) inteiro: i,n Inicio n ← tamanho(str) se (k = n) então escreve (str) /* Caso base */ senão /* Chamada recursiva */ para i de k até n repita TroqueCarateres( str, k, i) PermutaRecursiva(str, k + 1) TroqueCarateres( str, i, k) fim para fim se Fim /* Chamada principal*/ PermutaRecursiva(str,1)

Recursão - Na chamada recursiva : Para cada posição i de vetor de caracteres, entre k e o fim do vetor: 1. Troque os caracteres das posições i e k. 2. Gere todas as permutações do nível k+1, ou seja, todas as permutações de n-1 caracteres. 3. Restaure o vetor original trocando as posições i e k.

Recursão Exemplo 13- O algoritmo Merge Sort tem como objetivo a ordenação de um vetor A de n elementos reais. A sua estratégia é baseada no paradigma dividir para conquistar. 1. Passo Dividir Se um dado vetor A tem um ou nenhum elemento, simplesmente retorne pois ele já está ordenado. Caso contrário, divida A[p .. r] em dois subvetores A[p.. q] e  A[q + 1 .. r], cada um contendo a metade dos elementos de A[p .. r]. q é o índice do médio de A[p .. r]. 2. Passo Conquistar Ordene recursivamente os dois subvetores A[p .. q] e A[q + 1 .. r]. 3. Passo combinar ou intercalar Combine os elementos de volta em A[p .. r] mesclando os subvetores ordenados A[p .. q] e A[q + 1 .. r] em uma sequência ordenada. Para realiza esta etapa vamos definir um procedimento Merge (A, p, q, r).

Recursão Procedimento merge_sort (real: v[], inteiro: p, inteiro: r) Inicio inteiro: q se (p < r) então q ← (p + r) / 2; merge_sort (v, p, q); merge_sort (v, q, r); merge (v, p, q, r); fim se Fim

Recursão procedimento merge (real: v[], inteiro: p, inteiro: q, inteiro: r) Inicio inteiro: i, j,k, nv1,nv2,*w aloque(w, r-p) /*alocação dinâmica de memória que será melhor explicado no próximo tópico sobre apontadores*/ i ← p, j ← q+1 , k ← 0 enquanto (i <= q && j <= r) faça se (v[i] <= v[j]) então k ← k+1, i ← i+1, w[k] ← v[i] senão k ← k+1, j ← j+1,w[k] ← v[j] fim se fim enquanto enquanto (i < q) faça k ← k+1,i ← i+1, w[k] ← v[i] fim enquanto enquanto (j <= r) faça k ← k+1 j ← j+1, w[k] ← v[j] fim enquanto para i de p até r repita v[i] ← w[i-p] libera(w)

Recursão