Você está na página 1de 12

Algoritmos de Monte Carlo

Emanuel Bissiatti de Almeida


Abril 2022

1 Método de Monte Carlo


O método de Monte Carlo foi inventado por John von Neumann (1903-1957)
e Stanislaw Ulam (1909-1984) ([1]) durante desenvolvimento da bomba nuclear
de Hidrogênio. Ulam nasceu na Polônia e emigrou para os Estados Unidos em
1939, e a partir de 1943 ele trabalhou no Projeto Manhattan que culminou no
desenvolvimento da primeira bomba nuclear.
No ano de 1946, durante o desenvolvimento da bomba de Hidrogênio, os
cientistas estavam com dificuldades de encontrar a frequência de colisões dos
nêutrons para descobrir qual é a energia liberada em cada colisão. Apesar dos
cientistas saberem como encontrar uma solução para esse problema de uma
maneira determinística, os cálculos para tal resolução eram complicados, prin-
cipalmente em uma época sem computadores pessoais.
Inspirando no seu raciocínio do jogo de Paciência, Ulam desenvolveu o mé-
todo que hoje é conhecido como Monte Carlo. Em um jogo de paciência utiliza-se
um baralho convencional, com exceção dos coringas, contendo um total de 52
cartas na qual o objetivo é montar quatro pilhas de cartas, uma pilha para cada
naipe, em ordem crescente, seguindo as regras do jogo (que não serão descritas
neste artigo). Sabemos que o total de combinações dessas cartas é de 52!, porém
não são todos os jogos possíveis de solução: a montagem inicial do Paciência
depende da ordenação aleatória das cartas, e, seguindo a regra do jogo, algumas
ordenações não tem solução. Ulam, então, propôs a si mesmo um problema: ele
queria descobrir quantas partidas de Paciência tem solução. Esse desafio não
simples de resolver, Paciência tem um número grande de soluções Sabemos que
52! é um n sendo difícil descobrir uma solução de maneira determinística.
Felizmente Ulam, encontrou uma solução para esse problema, utilizando
da aleatoriedade inicial do baralho de cartas, ele jogou um número grande de
partidas de Paciência, e contabilizou o número de partidas em que foi possível
concluir em relação ao número total de partidas que ele jogou. Multiplicando
essa razão por 52! foi possível encontrar uma aproximação razoável da solução.
Assim, utilizando desse raciocínio, Ulam, com a ajuda do colega John von
Neumann, que aprovou a ideia, desenvolveram o método de Monte Carlo. Em
seguida, o físico greco-americano Nicholas Metropolis (1915-1999), que também
trabalhava na operação de pesquisa secreta, ficou responsável pela implementa-
ção do método num dos primeiros computadores programáveis: o ENIAC.

1
Como esse projeto era secreto, os cientistas precisavam de um codinome
para tal, Metrópolis, então sugeriu referenciar o famoso cassino do principado
de Mônaco, o Monte Carlo, local onde o tio de Ulam gastava o dinheiro da
família.

2 Exemplos do método de Monte Carlo


Resumidamente, o método de Monte Carlo [? ] é utilizado para encontrar
um resultado determinístico a partir de experimentos aleatórios independentes
entre si. Para melhorar as aproximações do resultado esperado, é recomendado
utilizar um número grande de experimentos aleatórios. Veja alguns exemplos a
seguir:

1. Encontrar o valor de π.
Vamos gerar aleatoriamente Pn pontos i.i.d. com coordenadas (xi , yi ) com
x ≈ U nif orme[−1, 1] e y ≈ U nif orme[−1, 1] para estimar o valor de π.
Considere um círculo de raio r = 1 centrado na origem que está inscrito
num quadrado de lado l = 2. Vamos estimar o valor de π comparando a
razão da quantidade de pontos Pc que "caíram"dentro do círculo (matema-
ticamente Pc representa o número de pontos em que a distância euclidiana
deles a origem é menor do que r) em relação ao toral de pontos P com a
razão da área do círculo em relação ao quadrado, observe:

πr2 Pc

l Pn
Utilizando os valores de r = 1 e l = 2, temos:

π Pc

4 Pn
Veja o nosso problema:

2
Figura 1: Estimar π a partir da área

Em pseudo código vamos resolver estimar o valor de π como:


P_c = 0 -- numeros de pontos que pertencem no circulo
P_n = 100 -- definir numero de pontos
r = 1 -- define o raio do circulo
i = 1
while i < P_n do
x = uniforme [ -1 ,1]
y = uniforme [ -1 ,1]
-- Verificar se distancia de P_i =( x , y ) e (0 ,0)
-- eh menor que r
if ( dist ({ x , y } ,{0 ,0}) <= r ) then
P_c ++
end
i ++
end
print ( P_c )
print (4* P_c / P_n ) -- valor estimado de pi

Utilizando a linguagem Lua e a biblioteca GnuPlot para gerar as imagens,


com o código a seguir obtive boas aproximações para π

3
local x,y = {} ,{}
local P_n = 100
local P_c = 0
local r = 1

math.randomseed (100)

function MyUniforme ()
-- Tranforma uma uniforme [0 ,1] em uniforme [ -1 ,1]
return 2* math.random () -1
end

for i = 1 , P_n , 1 do
local xi = MyUniforme ()
local yi = MyUniforme ()
-- Calcula a d i s t n c i a do ponro P_i do centro
if xi ^2+ yi ^2 <= r then
-- Caso verdadeiro P_i pertence ao c r c u l o .
P_c = P_c + 1
end
table.insert (x , xi )
table.insert (y , yi )
end

print ( P_c )
print (4* P_c / P_n )

gnuplot ({
--" set term " ,
" set grid " ,
" set parametric " ,
" set trange [ - pi : pi ] " ,
" set xrange [ -1 .5 :1 .5 ] " ,
" set yrange [ -1 .5 :1 .5 ] " ,
output = " pi2.png " ,
{ " t / pi ,1 lt rgb ' brown ' notitle " } ,
{ " t / pi , -1 lt rgb ' brown ' notitle " } ,
{ " 1 , t / pi lt rgb ' brown ' notitle " } ,
{ " -1 , t / pi lt rgb ' brown ' notitle " } ,
{ " cos ( t ) , sin ( t ) " } ,
data = {
x,
y,
},
{ using = ' 1:2 ' } ,})

4
Para: Pc = 100, π = 3.12, observe a figura:

Figura 2: Estimar π com 100 pontos

Fazendo o experimento com diferentes amostras de números aleatórios


obtive o seguinte resultado:

Pn Pc π
500 398 3.184
1000 774 3.096
2000 1579 3.158
3000 2391 3.188
4000 3147 3.147
5000 3869 3.0952
6000 4730 3.1533333333333
7000 5474 3.128
8000 6303 3.1515
10000 7850 3.14
20000 15671 3.1342
100000 78612 3.14448
1000000 785295 3.14118

Para Pc = 200, π = 3.14, observe a figura:

5
Figura 3: Estimar π com 200 pontos

2. Integração numérica
Semelhante ao que fizemos para encontrar o valor de pi usando a área
do círculo em relação ao quadrado, podemos utilizar para encontrar o
valor aproximado de uma integral de determinada função f (x) : I → R
estritamente positiva no intervalo de integração [a, b]. Com o seguinte
pseudo código podemos encontrar o valor aproximado de uma integral:
P_c = 0 -- numeros de pontos que pertencem a integral
P_n = 100 -- definir tamanho da amostra
a , b = 1 ,10 -- intervalo de integracao
f = function ( x ) -- define a funcao de interesse
i = 1
while i < P_n do
-- ponto aleatorio no intervalo [a , b ]
x = uniforme [a , b ]
-- uniforme entre 0 e o f ( x ) maximo em [] a , b ]
y = uniforme [0 , max (f ,a , b )]
-- Verificar se y eh menor que o f ( x ) da curva
if yi <= F ( xi ) then
P_c ++
end

6
i ++
end
print ( P_c )
-- valor estimado da integral de f em a , b
print ( max ( f )*( b - a )*( P_c / P_n ))

Por exemplo, seja f (x) = x1 , vamos calcular por Monte Carlo:


Z 10
1
dx
1 x

Como f é estritamente positiva e decrescente no intervalo, o ponto de


máximo ocorre no ponto x = 1 na qual f (1) = 1. A seguir o código Lua
utilizado para encontrar o valor da integral por Monte Carlo e para plotar
o gráfico da função.
local gnuplot = require ' gnuplot '
math.randomseed (10)

local x,y = {} ,{}


local P_n = 200
local P_c = 0
local a,b = 1 ,10

math.randomseed (100)

function F ( x )
return 1/ x
end

function UniformeAB (a , b )
-- Tranforma uma uniforme [0 ,1] em uniforme [a , b ]
return (b - a )* math.random ()+ a
end

for i = 1 , P_n , 1 do
local xi = UniformeAB (a , b )
local yi = UniformeAB (0 , F (1))
-- Verifica se yi eh menor que a curva
if yi <= F ( xi ) then
P_c = P_c +1
end
table.insert (x , xi )
table.insert (y , yi )
end

7
print ( P_c )
print (( b - a )*( P_c / P_n ))

gnuplot ({
" set grid " ,
" set xrange [1:10] " ,
" set yrange [0:1] " ,
output = " int1.png " ,
{ " 1/ x " } ,
{ using = ' 1:2 '} ,
data = {
x,
y,
},
})
R 10 1
Veja o experimento com 100 pontos, na qual: 1 x ≈ 2.3025

R 10 1
Figura 4: Estimar 1 x dx com 100 pontos

Realizando o experimento múltipla vezes com diferentes amostras aleató-


rias temos:

8
Pn Pc Integral
500 125 2.25
1000 222 1.998
2000 523 2.3535
3000 753 2.259
4000 999 2.24775
5000 1286 2.3148
6000 1485 2.2275
7000 1791 2.3027142857
8000 1999 2.248875
10000 2533 2.2797
20000 5105 2.29725
100000 25420 2.2878
500000 128097 2.305746

Em comparação, sabemos que o valor determinístico dessa integral é:


Z 10
1
dx = ln(10) = 2.3025...
1 x

3 Cadeias de Markov
A cadeia de Markov (em homenagem ao matemático Andrei Andreyevich Mar-
kov) é um processo estocástico com a propriedade de Markov (Um estado neste
contexto refere-se à atribuição de valores aos parâmetros). Isto é, cada estado
depende exclusivamente do estado anterior a este. Um exemplo simples para
entender essa ideia é tentar prever o clima do dia seguinte apenas com os dados
obtidos hoje. Se choveu hoje, podemos observar os dados históricos mostrando
a distribuição do tempo no dia seguinte à chuva para estimar as probabilidades
do tempo amanhã. O conceito de uma Cadeia Markov é que não precisamos
conhecer toda a história de um processo para prever a próxima produção, uma
modelagem que funciona bem em muitas situações do mundo real

4 MCMC Monte Carlo por Cadeias de Markov


Muitas das vezes quando trabalhamos com estatística bayesiana, dada uma de-
terminada distribuição a priori e os dados do problema, não é possível encontrar
uma distribuição a posteriori para o nosso problema. Buscando uma alternativa
a esse problema, os cientistas que trabalhavam no projeto Manhattan desenvol-
veram um algoritmo para resolver esse problema.
O MCMC [2] é um conjunto de métodos estatísticos que junta as duas ideias
anteriores deste artigo para gerar uma distribuição de probabilidade. MCMC vai
gerar repetidamente valores aleatórios para os parâmetros de uma distribuição
baseada nos valores atuais. Cada amostra de valores é aleatória (Monte Carlo),
mas as escolhas para os valores são limitadas pelo estado atual (Cadeias de

9
Markov) e pela suposta distribuição prévia dos parâmetros. O MCMC pode ser
considerado como uma caminhada aleatória que gradualmente converge para a
verdadeira distribuição.

5 Metropolis-Hastings algoritmo
O algoritmo nomeado como uma homenagem ao Metropolis que programou
Monte Carlo nos primeiros computadores, é um exemplo de utilização das téc-
nicas de MCMC em estatística. Veja a seguir uma breve descrição do algoritmo:
Vamos gerar uma cadeia de Markov X0 , X1 , ... utilizando Metropolis-Hastings
[3]:
Defina uma distribuição a priori g(.|Xt ).
Defina um valor inicial X0 , que pertence ao domínio de g.
Enquanto não convergir para uma distribuição estacionária repta:

1. Gere um valor candidato Y = Xt+1 a partir de g(.|Xt )


2. Gere u U nif orme[0, 1]
3. Calcule a taxa de aceitação :
f (Y )g(Xt |Y )
Se u ≤ min( , 1)
f (Xt )g(Y |Xt )
Xt+1 = Y
caso contrário
Xt+1 = Xt

Observe que neste caso só precisamos conhecer o núcleo da distribuição f .

6 Hamiltonian Monte Carlo


Esse é mais um método de Monte Carlo com Cadeia de Markov. Nele o obje-
tivo é obter uma sequência de amostras aleatórias que convergem para serem
distribuídas de acordo com uma distribuição de probabilidade alvo para a qual
a amostragem direta é difícil. Esta sequência pode ser usada para estimar inte-
grais em relação à distribuição alvo (valores esperados).
Hamiltoniano Monte Carlo [4] corresponde a uma modificação do algoritmo
Metropolis-Hastings, com uma evolução dinâmica Hamiltoniana simulada usando
um integrador numérico de preservação de volume e reversível no tempo utili-
zado para mover para um novo ponto de estado.
Não vou explicar em detalhes esse algoritmo (que está bem explicado em [5])
por ser mais complicado que o modelo de Metropolis-Hastings, entretanto, vou
usar a implementação desse algoritmo da biblioteca scilua para estimar o valor
esperado de uma distribuição:

10
jit.opt.start ( " loopunroll =60 " ) -- Set optimization flags :

-- Load required modules :


local math = require " sci.math " .generic
local diff = require " sci.diff "
local alg = require " sci.alg "
local prng = require " sci.prng "
local stat = require " sci.stat "
local mcmc = require " sci.mcmc "

local rng = prng.std ()


local N = 50000 -- 50000 samples ( after adaptation ) .
local theta0 = alg.tovec { 0 .5 ,0 .5 } -- Starting value of chain.
local mystat = stat.olcov (2) -- Compute mean and covariance.

-- Logarithm of target density :


local function logpdf ( x )
return 2* math.log ( x [1]) - x [1]* x [2]^2 - x [2]^2 + 2* x [2] - 4* x [1]
end
-- Automatic gradient computation ( vector of 2 elements ):
local dlogpdf = diff.gradientf ( logpdf , 2)

local start = os.clock () -- To time the simulation.


mcmc.nuts ( rng , dlogpdf , theta0 , {
stop = N,
olstat = mystat ,
})
local mu , sig = alg.vec (2) , alg.mat (2 , 2)
mystat : mean ( mu ) -- Compute mean of target density.
mystat : cov ( sig ) -- Compute covariance of target density.

print ( " mean : " )


print ( mu )
print ( " covariance : " )
print ( sig )
Observe que a média e a covariância da distribuição "logarithmic density"convergiu
usando o Hamiltonian Monte Carlo para:
mean: +0.652321 +0.631088
covariance: +0.154171,-0.050681 -0.050681,+0.334201

Referências
[1] Coluna Viana Folha. Matemática dos cassinos resolve muitos problemas prá-
ticos. https://www.cos.ufrj.br/~daniel/mcmc/Coluna_Viana_Folha.pdf.

11
[2] Will Koehrsen. Mcmc in python. https://towardsdatascience.com/
markov-chain-monte-carlo-in-python-44f7e609be98.
[3] Fernando P. Maye. Mcmc. http://cursos.leg.ufpr.br/ce227/MCMC-1.
html.

[4] Wikipedia. Hamiltonian monte carlo. https://en.wikipedia.org/wiki/


Hamiltonian_Monte_Carlo.
[5] Matthew D. Hoffman. The no-u-turn sampler: Adaptively setting path
lengths in hamiltonian monte carlo. https://stat.columbia.edu/~gelman/
research/published/nuts.pdf.

12

Você também pode gostar