Recursividade e Funções Recursivas
Aleatoriedade
e Processos de Monte Carlo
Pedro Barahona
DI/FCT/UNL
Introdução aos Computadores e à Programação
2º Semestre 2007/2008
Funções Encadeadas
•
Como foi visto em vários exemplos anteriores, na especificação de uma função
podem ser chamadas outras funções, que por sua vez podem chamar outras
funções.
•
Assim uma função (e em último caso, um programa) pode ser vista como um
encadeamento de funções. Por exemplo, a função ang_vec/2 chama a função
mod_vec/2 na especificação abaixo indicada
function a = ang_vec(V1,V2); % retorna ângulo em graus
M = prod_int(V1,V2))
cos = M /(mod_vec(V1) * mod_vec(V2))
a = acos(cos)*180/pi
endfunction
•
Em algumas linguagens de programação, em vez de funções são usados
procedimentos, mas a filosofia de encadeamento é semelhante.
•
De notar que o programa principal, pode ele próprio ser visto como uma função.
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
2
Funções Recursivas
•
Um caso particular ocorre quando as funções se chamam a si próprias, isto é,
quando as funções são recursivas.
•
Um exemplo muito simples é o da função factorial, que pode ser definida
(incompletamente) como
fact(n) = n * fact(n-1)
•
Nestas condições, tal como nos ciclos, levanta-se o problema da terminação. Se uma
função se chama a si própria, existe o risco de a computação se tornar infinita (entrar
em ciclo fechado ou “loop”).
•
É pois condição necessária para evitar estes ciclos infinitos que sejam definidas e
testadas em primeiro lugar as condições de fim da recursividade.
se n  0
1

fact(n)  
n * fact(n  1) se n  0
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
3
Funções Recursivas
•
Em geral, a recursividade é feita com base num conjunto recursivo (indutivo), definido
através de cláusulas
– de base; um ou vários elementos de base (que fecham a recursão)
– de recursão: uma definição recursiva que permite a obtenção de elementos a
partir de outros elementos.
•
Num grande número de casos, o conjunto recursivo utilizado é o conjunto dos
numeros inteiros, em que
– 1 (ou 0) é um número inteiro (cláusula de base)
– Se i é inteiro, i+1 também é inteiro (cláusula de recursão)
•
Tendo em conta esta estrutura recursiva, podemos definir (correctamente) a função
factorial tendo em conta a cláusula de base e a cláusula recursiva:
se n  0
1

fact(n)  
n * fact(n  1) se n  0
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
4
Funções Recursivas
•
No caso da função factorial basta então testar inicialmente se o parâmetro de entrada
é nulo. Em pseudo-código, a função é definida como
função fact(n)
se n = 0 então
fact  1
% elemento base
senão
fact  n * fact(n-1) % definição recursiva
fimse;
fimfunção;
•
Em Octave é definida de uma forma semelhante
function f = fact(n)
if n == 0
f = 1
else
f = n*fact(n-1)
endif;
endfunction;
2 Abril 2008
se n  0
1

fact(n)  
n * fact(n  1) se n  0
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
5
Limites à Recursividade
•
De notar que para prevenir os ciclos infinitos, o Octave tem uma variável prédefinida,
max_recursion_depth, que limita o número de chamadas recursivas de uma função.
•
Por omissão (“default”), o valor da variável é 256, pelo que não se pode calcular
fact(260) sem alterar o valor da variável.
•
Existem várias outras funções recursivas em que pode existir mais do que um
elemento base ou em que o conjunto recursivo é mais difícil de definir. Em qualquer
caso é essencial definir a condição de terminação.
•
Para exemplificar estas funções, estudamos de seguida
– a determinação dos números de Fibonacci e
– o cálculo do maior divisor comum entre dois números.
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
6
Função Fibonacci (Recursiva)
•
Fibonacci (matemático da Renascença italiana) estabeleceu uma série curiosa de
números para modelar o número de casais de coelhos em sucessivas gerações. Em
cada geração, o número pode ser obtido através dos das 2 gerações anteriores
através de
fib(n) = fib(n-1) + fib(n-2)
•
Assumindo que nas primeiras duas gerações só existe um casal de coelhos, os
sucessivos números de Fibonacci são
1,1,2,3,5,8,13,21,34,55,89, ...
•
Estes números ocorrem em vários processos biológicos (e não só), por exemplo, no
número de pétalas de algumas flores.
•
A sua determinação recursiva impõe o cálculo directo do valor para 2 elementos de
base (a 1ª e a 2ª geração).
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
7
Função Fibonacci (Recursiva)
•
Em Octave, a obtenção dos números de Fibonacci pode ser feita muito facilmente
através da função recursiva abaixo (em Octave)
function f = fib(n);
if n == 1
f = 1;
elseif n == 2
f = 1;
else
f = fib(n-1) +
endif;
endfunction;
•
% 1º elemento base
% 2º elemento base
% definição recursiva
fib(n-2);
Como é fácil de constatar, se o número n inicial for maior ou igual a 1, a recursão
termina. No entanto, se se chamar fib(0) a computação não termina (em Octave
termina quando o número de chamadas recursivas exceder o valor da variável
max_recursion_depth)!
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
8
Máximo Divisor Comum (Função Recursiva)
•
Como se sabe, o máximo divisor comum (mdc) entre dois números m e n é também
um divisor da sua diferença, m-n.
•
Por exemplo, o mdc de 60 e 36 é 12, que divide 24 = 60-36.
•
Por outro lado, o mdc dos dois números m e n é ainda o mdc do menor número (n)
com a diferença (m-n). Se houvesse um divisor comum maior, ele seria igualmente
divisor de n, contrariamente à hipótese.
•
Assim, pode determinar-se o mdc de dois números através da determinação do mdc
de números cada vez menores. A computação termina quando os números forem
iguais, caso em que o mdc é esse número.
•
Por exemplo:
60 - 36 = 24 ;
36 – 24 = 12;
24 – 12 = 12;
12 = 12 !
2 Abril 2008
% 12 é o mdc de 60 e 36
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
9
Máximo Divisor Comum (Função Recursiva)
•
Em Octave, pode determinar-se o maior divisor comum de dois números com a
função seguinte
function d = mdc(m,n);
if m == n
d = m;
elseif m > n
d = mdc(m-n,n);
else
d = mdc(n-m,m);
endif;
endfunction;
•
De notar que antes da chamada recursiva deve ser verificado qual o maior dos
argumentos de entrada (para não se chamar a função com argumentos negativos!)
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
10
Recursão e Iteração
•
Uma função definida recursivamente pode ser igualmente definida de uma forma
iterativa (através de ciclos). Em geral, a definição recursiva é mais “declarativa” na
medida em que explicita o que se pretende obter e não a forma como se obtém (ou
seja, um determinado programa que é usado).
•
Por outro lado, uma definição iterativa, embora não permita uma compreensão tão
imediata, é geralmente mais eficiente, já que as instruções de programação de baixo
nível para a iteração são mais eficientes que as de chamadas de funções.
function f = fact_ite(n);
f = 1;
for i = n:-1:1
f = f * i;
endif;
endfunction;
•
function f = fact_rec(n);
if n == 1
f = 1
else f = n * fact_rec(n-1);
endif;
endfunction;
No entanto, estas diferenças são geralmente pouco importantes, excepto em casos
de recursão múltipla, em que a ineficiência pode ser “catastrófica”.
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
11
Recursão e Iteração
•
Esta é a situação da função de Fibonacci, em que o seu valor depende de 2
chamadas recursivas. Como as chamadas são independentes, a mesma função
acaba por ser recalculada várias (muuuuu...itas !) vezes.
•
Na realidade, o número de funções chamadas é o próprio número de fibonacci (7/1,
6/1, 5/2, 4/3, 3/5, 2/8) que aumenta exponencialmente com o valor de n.
7
5
6
5
4
3
2
2 Abril 2008
1
3
3
2
4
4
2
1
2
3
2
1
2
3
2
2
1
1
function f = fib(n);
if n <= 2 f = 1
else f = fib(n-1)+fib(n-2);
endif;
endfunction;
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
12
Recursão e Iteração
•
Para se ter uma ideia do aumento, podemos notar que apesar de inicialmente
pequenos
n
fib(n)
1
1
2
1
3
2
4
3
5
5
6
8
7
13
8
21
9
34
10
55
11
89
os números tornam-se rapidamente “grandes” !!!
n
fib(n)
26
121 393
27
196 418
28
317 811
29
514 229
30
832 040
31
1 346 269
32
2 178 309
ou mesmo “enormes” !!!
n
fib(n)
46
1 836 311 903
47
2 971 215 073
48
4 807 526 976
49
7 778 742 049
50
12 586 269 025
tornando proibitiva a sua computação recursiva (normal).
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
13
Memorização
•
Por vezes é possível aliar a declaratividade da versão recursiva, com a eficiência da
versão iterativa, através da memorização dos valores já computados. Por exemplo
se se pretenderem os primeiros 100 números de fibonacci, pode criar-se um vector,
fib_vec, com os valores já determinados.
– Se fib_vec(n) ainda não contiver o valor de fib(n), então determina-se fib(n) e
regista-se esse valor em fib_vec(n).
– Caso contrário, retorna-se simplesmente o valor de fib_vec(n).
•
Para que este esquema seja posível, é necessário que a tabela fib_m seja visível por
todas as instâncias da função fib(n). Para evitar passá-la como parâmetro deve
declarar-se como uma variável global.
•
Em Octave, para que uma variável seja global, ela deve ser declarada no programa
principal com a declaração global. No caso actual, se se pretende um vector linha
com 100 elementos inicializados a zero, devemos declarar a variável global
global fib_vec = zeros(1,100)
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
14
Memorização
•
Uma vez declarada uma variável global, ela só pode ser eliminada através da
declaração clear. Se pretendermos um vector com 200 elementos deveremos fazer
clear fib_vec
global fib_vec = zeros(1,200)
•
Para que na função a variável, fib_vec considerada seja a variável global e não uma
variável local, a variável deve ser identificada novamente como global.
function f = fib_mem(n); % versão recursiva com memória
global fib_vec;
if fib_vec(n) == 0
% se o valor ainda não foi calculado
if n <= 2
% determina esse valor ...
fib_vec(n) = 1;
else
fib_vec(n) = fib_mem(n-1)+fib_mem(n-2);
endif
endif;
f = fib_vec(n); % e atribui o valor memorizado à função
endfunction;
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
15
Aleatoriedade
•
Em muitas aplicações informáticas, nomeadamente em simulações que utilizam
processos aleatórios ou estocásticos, muito utilizadas em engenharia.
•
Um processo aleatório é aquele cujo resultado não é conhecido com rigor, mas que
obedece a uma distribuição de probabilidades.
•
Por exemplo, no lançamento de uma moeda ao ar, não se conhece o resultado a
priori, mas, se a moeda não estiver viciada, assume-se que cerca de 50% das vezes
sai cara, enquanto que nas outras cerca de 50% de vezes sai coroa. Já o número de
vezes que no lançamento de um dado sai um “2” é de 1/6 (ou 16.66%).
•
Tais processos podem ser simulados com recurso a números (pseudo-) aleatórios,
conhecidos como processos ou simulações de Monte Carlo.
•
Estes números são obtido por um gerador de números aleatórios, que no caso do
Octave é invocado pela função pre-definida rand(), ou simplesmente rand.
•
Esta função retorna um número aleatório entre no intervalo [0, 1[ de cada vez que é
invocada.
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
16
Exemplos: Moeda ao ar e Lançamento de um Dado
•
Por exemplo o processo de n lançamentos de uma moeda ao ar, em que 1 significa
caras e 2 significa coroas, pode ser simulado pela função
function L = moeda(n);
for i = 1:n
if rand > 0.5 L(i) = 1
else
L(i) = 2
endif
end for
endfunction
•
Já o lançamento de dados pode ser simulado pela função
function L = dado(n);
for i = 1:n
r = rand;
if
r < 1/6
elseif r < 2/6
...
elseif r < 5/6
else
endif
end for
endfunction
2 Abril 2008
L(i) = 1;
L(i) = 2;
L(i) = 5
L(i) = 6
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
17
Exemplos: Moeda ao ar e Lançamento de um Dado
•
Na realidade, para grandes números a frequência aproxima-se da probabilidade, isto
é, se lançarmos 10 vezes uma moeda ao ar, o número de caras deve ser próximo de
5, pois a probabilidade de sair caras é ½ e ½ *10 = 5.
•
Em geral a frequência de ocorrrência de um valor aleatório aproxima-se da sua
probabilidade para grandes valores de n. Mais formalmente, denotando por
– p(X=a) ou simplesmente pa, a probabilidade de um evento X tomar o valor A;
– n o número de eventos e
– ea o valor esperado do número de ocorrências de X=a
– fa o número efectivo de ocorrências de X=a
temos
ea = n*pa ; e
fa ≈ ea,
devendo o erro relativo (n) = ( fa – ea)/ n diminuir com o aumento do número de
eventos.
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
18
Passeio Aleatório
Exemplo: Uma pessoa pretende percorrer um passeio entre as duas vias de uma estrada,
em que começando no centro do passeio, pode guinar até k vezes para a direita ou a
esquerda antes de sair do passeio e ser atropelada. Estando “bêbada”, em cada passo
que dá tem uma probabilidade de 50% de guinar para a direita .e outra igual de guinar
para a esquerda. Sabendo-se que deverá dar n passos, qual a probabilidade de chegar
ao fim da estrada sem sair do passeio?
•
Para resolver este problema, basta utilizar a função passeio, que para um número total
de percursos de n passos num passeio que admite k desvios, conta o número na de
“atropelamentos”.
function p = passeio(n,k,total);
na = 0;
for i = 1:total
na = na + atropelado(n,k);
endfor
p = na/total
endfunction
•
Naturalmente a função passeio utiliza uma função atropelado que deve retornar 1 se
houver atropelamento e 0 no caso contrário.
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
19
Passeio Aleatório
•
Para a função atropelado, vamos considerar uma variável desvio, que começa em 0 e
vai acumulando os desvios para a direita e para a esquerda.
•
Se conseguir chegar ao fim do ciclo for sem atingir um desvio, em módulo, maior que
k, a função retorna 0.
•
De notar que logo que o desvio seja maior que k, a função pode retornar
imediatamente o valor 1, saindo do ciclo for com a instrução de excepção return.
function p = atropelado(n,k);
p = 0; desvio = 0;
for i = 1:n
if rand > 0.5 d = 1; else d = -1; endif;
desvio = desvio + d;
if abs(desvio) > k
p = 1;
return;
endif
endfor
endfunction
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
20
Área de uma Curva
• Os numeros aleatórios também podem ser utilizados na determinação aproximada de
áreas. O raciocínio é o seguinte:
1. Contornemos a área a determinar A, por uma área conhecida, B
2. Consideremos um conjunto aleatório de pontos na área conhecida, e contabilizar
a percentagem p de pontos que “cai” dentro da área a determinar.
3. A área A pode ser aproximada por A ≈ pB .
• Exemplo:
f ( x)  1  x 2
1
B
1. Calculemos a área do ¼ de circulo de raio 1 ao lado.
2. A área B é naturalmente de 1
A
3. A área do ¼ de circulo deve ser próxima de /4
1
• Falta determinar a forma de gerar pontos aleatórios no quadrado, o que se pode fazer
facilmente gerando x e y aleatórios no intervalo [0,1[ e verificando se ficam abaixo da
função y = sqrt(1-x2)
2 Abril 2008
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
21
Área de uma Curva
• Este algoritmo está implementado na função pi_prob abaixo indicada.
– O ciclo for gera n pontos <x,y>,
– Destes n pontos , c estão abaixo da curva do ¼ círculo.
– A área do ¼ de círculo é a fracção c/n da área do quadrado (1*1)
– O valor de pi é 4 vezes a área
function p = pi_prob();
c = 0;
for i = 1:n;
x = rand(); y = rand();
if y < sqrt(1-x^2)
c = c+1;
endif
endfor
area = (1*1)* c/n;
p = 4*area;
endfunction
2 Abril 2008
f ( x)  1  x 2
1
B
A
Recursividade Funções Recursivas. Aleatoriedade e Processos de Monte Carlo
1
22
Download

Document