Algoritmos Computacionais

Artigos sobre lógica de programação por Tiago Madeira

algoritmo: do Lat. algorithmos < Ár. alkharizmi: [Inform.] conjunto de etapas bem definidas necessárias para chegar à resolução de um problema.

Recursão

January 8, 2006

A recursão é uma das técnicas mais simples e úteis que existem para usarmos em nossos algoritmos. Consiste em uma função (denominada recursiva) chamar a si mesmo, até que o retorno seja trivial. Resolvi abordá-la aqui porque alguns algoritmos que estudaremos mais para frente usam funções recursivas.

Gostaria de, antes de falar sobre o assunto, contar um pouco da minha história com recursão, porque foi meu início no mundo dos algoritmos.

Junho de 2004, tinha eu 13 anos e fui premiado com medalha de ouro na modalidade iniciação da Olimpíada de Informática. Fui convidado para o curso de introdução a programação na UNICAMP e, extremamente geek como eu era (e ainda sou), falei para os professores que já programava em C e então eles sugeriram que eu fosse para o curso de programação avançada.

No entanto, eu ainda achava muito complicado os algoritmos da modalidade de programação da OBI, por isso pedi pra ficar num "meio-termo". Eles toparam numa boa e com isso passei a ter aulas com um monitor excelente (aluno da UNICAMP) chamado Ribamar. Esse cara foi extremamente importante para minha iniciação na programação de verdade.

Na primeira tarde que tive aula com ele, ele perguntou se eu sabia o que era recursão. Respondi que não e, a partir daquele dia e depois de ele me ensinar também de grafos e programação dinâmica, além de me apresentar o Slackware, me tornei um amante de algoritmos. A recursão, portanto, mesmo sendo algo simples, é um assunto especial pra mim porque representa a mudança de "nível".

Então, mãos à obra!


Em matemática, o número fatorial de LaTeX: n é igual a: LaTeX: n \times{} n-1 \times{} n-2 \times{} \ldots{} 2 \times{} 1.

Logo, por exemplo, LaTeX: 5! (cinco fatorial) seria igual a: LaTeX: 5 \times{} 4 \times{} 3 \times{} 2 \times{} 1 = 120.


Um exemplo bom e simples de recursão é um algoritmo para determinar números fatoriais:

1. função fatorial (n)
2.	se LaTeX: n = 1, então
3.		retorna LaTeX: 1
4.	senão
5.		retorna LaTeX: n times{} fatorial(n-1)
6.	fim-se
7. fim-função

Domínio de nossa função: LaTeX: n \in{} \mathbf{N} / n \geq{} 1.

Qual o custo desse algoritmo?

Vamos abrir um grande parênteses aqui até a próxima linha horizontal para descobrir qual o custo do nosso algoritmo antes de continuar com a conversa sobre recursão e relembrar/reforçar o post sobre Análise de Algoritmos. Vou colocar o número de vezes que cada instrução é executada, usando o esquema que será o padrão para as próximas vezes que veremos custos:

Número da linha: Número de vezes que é executada..

  1. LaTeX: n
  2. LaTeX: n-1
  3. LaTeX: 1
  4. LaTeX: n-1
  5. LaTeX: n-1

LaTeX: T(n) = (n) + (n-1) + (1) + (n-1) + (n-1) = 4n - 2

Uma função linear, o tipo de algoritmo mais simples que podemos encontrar, com excessão dos que são uma função constante. Mas o parênteses na verdade não serviu só pra isso. Eu queria aproveitar pra escovar uns bits de nosso código. Você percebeu que o primeiro condicional é executado LaTeX: n-1 vezes, mas só entramos nele uma vez? Então vamos inverter nosso condicional.

1. função fatorial (n)
2.	se LaTeX: n > 1, então
3.		retorna LaTeX: n times{} fatorial(n-1)
4.	senão
5.		retorna LaTeX: 1
6.	fim-se
7. fim-função

Novo custo

  1. LaTeX: n
  2. LaTeX: n-1
  3. LaTeX: n-1
  4. LaTeX: 1
  5. LaTeX: 1

LaTeX: T(n) = (n) + (n-1) + (n-1) + (1) + (1) = 3n

Claro que continua uma função linear, não houve nenhuma grande mudança. Os dois continuam com a mesma ordem de crescimento e tal... LaTeX: 4n+2 comparado com LaTeX: 3n é uma diferença pequena, mas essa solução ficou bem mais elegante. ;) Poxa, diminuímos o custo do algoritmo em LaTeX: \frac{1}{4}! Hehehe...

Agora, antes de continuar, só vamos definir a notação assintótica desse nosso novo custo!

A fórmula do LaTeX: \Theta{} é: LaTeX: 0 \leq{} c_{1} g(n) \leq{} f(n) \leq{} c_{2} g(n)

Substituindo pela nossa função, temos: LaTeX: c_{1}n \leq{} 3n \leq{} c_{2}n. É trivial, que podemos escolher para as duas constantes LaTeX: c_{1}=c_{2}=3 e para LaTeX: n_{0}=0. Com isso pretendi mostrar-lhes uma conclusão óbvia que no outro artigo não tinha mostrado para não complicar muito: uma função reta (linear) pertence sempre a notação LaTeX: \Theta{}(n) e uma função quadrática pertence sempre a notação LaTeX: \Theta{}(n^{2}) (ora, façam um gráfico das funções e vejam se isso não é óbvio!). Mas vamos aprendendo mais sobre análise de algoritmos com o tempo...


Bom... Continuando com a recursão... Nossa função cria um loop consigo mesma, ao invés de usar um para (for) ou enquanto (while). Ela se repete diminuindo um de LaTeX: n a cada iteração, até que chegue ao valor mínimo LaTeX: 1 aonde a resposta é trivial: LaTeX: 1.

O que é necessário para criarmos uma recursão? Apenas um ponto de parada! Temos que tomar cuidado para não criarmos loops infinitos cuidando sempre com cada entrada que o usuário pode colocar. Nesse caso, eu determinei que o domínio da função é LaTeX: n \in{} \mathbf{N} / n \geq{} 1. Se o cara colocasse LaTeX: n=0, minha função iria diminuindo infinitamente... e nunca iria retornar nada!

Para fazer a recursão portanto, precisamos analisar o domínio de nossa função e mais: precisamos conhecer um valor (que vai ser o limite; no caso do fatorial, o valor que sabíamos é que LaTeX: 1! = 1).

Acredito que vocês tenham achado tudo simples e que não tenham problema com isso. Funções recursivas vão ser extremamente úteis para nós nos próximos artigos. Vou finalizar mostrando-lhes alguns casos básicos de algoritmos em que podemos usar a recursão:

Números de Fibonacci

1. função fibonacci (n)
2.	se LaTeX: n geq{} 3, então
3.		retorna LaTeX: fibonacci(n-1) + fibonacci(n-2)
4.	senão
5.		retorna LaTeX: 1
6.	fim-se
7. fim-função

Domínio de fibonacci(n): LaTeX: n \in{} N / n \geq{} 1

Depois descobriremos como calcular os números de Fibonacci mais rápido, mas por enquanto nosso objetivo é a recursão!

Substituir um loop

Vamos supor que você quer imprimir os números de n a 1 e esqueceu a sintaxe do para... :D

1. função imprime_ate (n)
2.	imprima LaTeX: n
3.	se LaTeX: n>1, então
4.		imprime_ate(n-1)
5.	fim-se
6. fim-função

Domínio de imprime_ate(n): LaTeX: n \in{} N / n \geq{} 1

Todo loop pode ser uma recursão e tem alguns que ficam bem mais fáceis se forem! Nesse caso, é claro que seria mais simples usarmos um para!

Outros exemplos, para concluir

  • Ordenação por Intercalação (Merge Sort)
  • Busca em Profundidade (Depth-First Search) em Grafos
  • Union/Find
  • ... entre vários outros casos!

7 comentários

#1 | Fernando Sagaz (02/04/2006)

Mto boa explicação ! Estava procurando material sobre recursão e este é bem explicativo.

[...] O artigo está em outro local agora: Recursão [...]

#3 | Jose Cabicho (11/09/2007)

Gostei d ter gasto alguma da minha atencao neste artigo
saio do artigo satisfeito
gostei imenso dos exemplos
bem gostei

#4 | Júlio Kamizato (18/09/2008)

Não tenho certeza, mas acho que a recursão do Fibonacci está errada, o if tem que ser n>=2 e não >=3.

Abraço

#5 | Andrea (18/06/2010)

Kamizato, você certamente se esqueceu de ler a linha abaixo do algoritmo, que definia o domínio da função para n >= 1.
Com isso, o primeiro elemento é 1, o segundo é 2, e, claramente, neste caso utiliza-se o if n >= 3…
Se os elementos tivessem se iniciado do zero, seu comentário estaria correto.

#6 | suzyanne (13/07/2010)

alguem tem o algoritmo do jogo da dama
/???????

#7 | Moacir (10/08/2010)

A informação “função linear, o tipo de algoritmo mais simples que podemos encontrar, com excessão dos que são uma função constante.” está errada.
Existem algoritmos que são caracterizados, por exemplo, por uma função de complexidade *logarítmica*, que possui maior eficiência assintótica do que a linear.

Escreva um comentário

Dados pessoais

Seu e-mail não será publicado, mas você deve informá-lo para o autor poder responder seu comentário.

HTML 4.01 Strict: Você pode usar as seguintes tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Para ler o que escrevo atualmente, visite meu novo blog.

Índice

Online Judges

Programming Contests

Sobre o design

Este design foi copiado do CSS Zen Garden e modificado com autorização de seu autor, Gunta Klavina.

Licença

Todo o conteúdo deste site (incluindo textos, imagens, arquivos de áudio e quaisquer outros trabalhos), exceto quando especificado o contrário, está licenciado por Tiago Madeira sob uma Licença Creative Commons que permite que você copie, distribua, exiba, execute a obra e crie obras derivadas desde que mantenha os créditos, não use sua modificação para fins comerciais e compartilhe seu trabalho derivado pela mesma licença.

HTML 4.01 gerado por WordPress em 0.346 segundos.