Монте Карло методе
Монте Карло методе, или Монте Карло експерименти, представљају широку класу рачунарских алгоритама који се ослањају на поновљено случајно узорковање ради добијања нумеричких резултата. Основни концепт је коришћење случајности за решавање проблема који би у принципу могли бити детерминисани.
Монте Карло методе се углавном користе у три различите класе проблема: * оптимизација, * нумеричка интеграција, * генерисања узорака из расподела вероватноће.
Монте Карло методе се често примењују коришћењем рачунарских симулација и могу пружити приближна решења проблема који су иначе нерешиви или превише сложени за математичку анализу. Монте Карло методе такође имају нека ограничења и изазове, као што су компромис између тачности и рачунарског трошка, проблем димензионалности, поузданост генератора случајних бројева и верификација и валидација резултата.
У наставку су два уобичајена примера, Закон великих бројева (ЗВБ) и Централна гранична теорема (ЦГТ), који користе Монте Карло симулациону методу.
Централна гранична теорема (ЦГТ)
Теорема: Нека је \((X_n)\) низ независних случајних величина са истом расподелом, математичким очекивањем \(EX_1=\mu\) и коначном дисперзијом \(DX_1 = \sigma^2\). Ако је \(S_n=\sum\limits_{k=1}^n X_k\), онда за свако \(x\in\mathbb{R}\) при \(n\to\infty\) важи \[P\left\{\frac{S_n - n\mu}{\sigma\sqrt{n}}\le x\right\} \to \frac{1}{\sqrt{2\pi}}\int_{-\infty}^x e^{-\frac{t^2}{2}}dt,\] односно \[\frac{S_n - ES_n}{\sqrt{DS_n}} \to X^*, \quad X^*\in \mathcal{N}(0,1).\]
# Central Limit Theorem Simulation
# n: sample size of each sample
# dist: underlying distribution where the sample is drawn
simulation <- function(n, dist=NULL, param1=NULL, param2=NULL) {
r <- 10000 # Number of replications/samples
# This produces a matrix of observations with
# n columns and r rows. Each row is one sample:
my.samples <- switch(dist,
"E" = matrix(rexp(n*r,param1),r),
"N" = matrix(rnorm(n*r,param1,param2),r),
"U" = matrix(runif(n*r,param1,param2),r),
"P" = matrix(rpois(n*r,param1),r),
"C" = matrix(rcauchy(n*r,param1,param2),r),
"B" = matrix(rbinom(n*r,param1,param2),r),
"G" = matrix(rgamma(n*r,param1,param2),r),
"X" = matrix(rchisq(n*r,param1),r),
"T" = matrix(rt(n*r,param1),r))
all.sample.sums <- apply(my.samples,1,sum)
all.sample.means <- apply(my.samples,1,mean)
all.sample.vars <- apply(my.samples,1,var)
par(mfrow=c(2,2))
hist(my.samples[1,], col="gray", main="Distribution of One Sample", xlab="")
hist(all.sample.sums, col="gray", main="Sampling Distribution of the Sum", xlab="")
hist(all.sample.means,col="gray", main="Sampling Distribution of the Mean", xlab="")
hist(all.sample.vars,col="gray", main="Sampling Distribution of the Variance", xlab="")
}
simulation(n=1000, dist="P", param1=1)Закон великих бројева (ЗВБ)
Теорема: Нека је \((X_n)\) низ случајних величина дефинисаних на истом простору вероватноћа. Нека је \(S_n = \sum\limits_{k=1}^n X_k\). Ако \(\frac{S_n-ES_n}{n}\) конвергира скоро сигурно (у вероватноћи) ка нули када \(n\to\infty\), онда важи јаки (слаби) закон великих бројева за низ \((X_n)\).
set.seed(1212)
n = 50000
x = sample(0:1, n, repl = TRUE)
s = cumsum(x)
r = s/(1:n)
plot(r, ylim = c(0.4, 0.6), type = "l",
xlab = "tosses", ylab = "probability of heads up", main = "LLN simulation graph")
lines(c(0,n), c(0.5, 0.5))## x s r
## [1,] 1 1 1.00000
## [2,] 1 2 1.00000
## [3,] 0 2 0.66667
## [4,] 0 2 0.50000
## [5,] 1 3 0.60000
## [6,] 1 4 0.66667
## [7,] 1 5 0.71429
## [8,] 1 6 0.75000
## [9,] 0 6 0.66667
## [10,] 1 7 0.70000
## [1] 0.5013
Апроксимација броја \(\pi\)
Посматрајмо квадрат \(\mathcal{S}\subset \mathbb{R}^2\) димензије \(2\times 2\) у који је уписана кружница \(\mathcal{D}\) полупречника 1.
Тада је \[\frac{\iint_{\mathcal{D}}dx_1dx_2}{\iint_{\mathcal{S}}dx_1dx_2} = \frac{\pi}{4}.\]
Претходне интеграле можемо оценити симулацијама јер је \[\frac{\iint_{\mathcal{D}}dx_1dx_2}{\iint_{\mathcal{S}}dx_1dx_2} = \iint_{\mathcal{S}} \mathbb{I}((x_1,x_2)\in\mathcal{D}) \frac{1}{4}dx_1dx_2 = \mathbb{E}[\phi(X_1,X_2)] = \theta.\]
Да бисмо генерисали униформну расподелу на квадрату \(\mathcal{S} = (-1,1)\times (-1,1)\), можемо посматрати \[X_1 = 2U_1 - 1, \, X_2 = 2U_2-1,\] где је \(U_1,U_2\in\mathcal{U}(0,1)\).
# Number of points to generate
n <- 10000
# Generate random points
set.seed(123)
xs <- runif(n, min=-1, max=1)
ys <- runif(n, min=-1, max=1)
# Calculate distance from (0,0) and check if inside the unit circle
in.circle <- xs^2 + ys^2 <= 1^2
# Estimate π
mc.pi <- (sum(in.circle)/n)*4
mc.pi## [1] 3.1576
# Plot the points
plot(xs,ys,pch='.',col=ifelse(in.circle,"blue","grey")
,xlab='',ylab='',asp=1,
main=paste("MC Approximation of Pi =",mc.pi))Модел динамике популације
Основни модел динамике популације: на почетку прве године, бројност неке житарице износи 1000 јединки и расте просечним фактором од 1.1 годишње (процеси репродукције и смрти резултују стопом раста од \(10\%\)) пре жетве. Међутим, стопа раста варира насумично тако да сваке године, фактор раста од 1.1 има варијабилност која се уводи малим променама у процесима преживљавања и репродукције. Моделујте те варијације као лог-нормалне случајне променљиве. Након производње, \(8\%\) популације се уклања жетвом сваке године. Симулирајте бројност популације на крају сваке године током наредних 100 година.
nt = 100 # number of years
N = NULL # container for abundance
N[1] = 1000 # first end-of-year abundance
for (t in 2:nt) {
# N this year is N last year * growth *
# randomness * fraction that survive harvest
N[t] = (N[t-1] * 1.1 * rlnorm(1, 0, 0.1)) * (1 - 0.08)
}
plot(N, type = "l", pch = 15, xlab = "Year", ylab = "Abundance")Често, симулације издвајамо у посебене функције како бисмо их репродуковали одређени број пута.
pop_sim = function(nt, grow, sd_grow, U, plot = F) {
N = NULL # empty flexible vector container
N[1] = 1000
for (t in 2:nt) {
N[t] = (N[t-1] * grow * rlnorm(1, 0, sd_grow)) * (1 - U)
}
if (plot) {
plot(N, type = "l", pch = 15, xlab = "Year", ylab = "Abundance")
}
N
}## [1] 1000.0000 815.4564 815.4167 817.0242 865.3543 1004.3206 1113.3513
## [8] 1043.4588 1117.4382 1139.5047 1212.2803 1179.8882 1116.3126 1251.9174
## [15] 1281.0640 1387.0378 1316.4175 1010.1124 975.4666 1001.4428 1239.0185
## [22] 1250.0057 1007.1856 971.3801 890.4844 947.6862 934.8841 1008.8605
## [29] 1117.3076 1030.6148 1159.2436 1091.4611 1084.5315 1058.8098 868.2929
## [36] 847.8565 697.4140 607.6462 627.3836 656.5371 711.3591 673.7285
## [43] 716.5796 813.3450 786.1690 841.8792 721.0427 702.2719 710.0972
## [50] 756.2345 697.2046 687.6382 704.1375 780.0096 854.7118 850.8419
## [57] 886.5550 923.4473 864.1649 1109.7360 1146.7963 1217.7398 1078.7320
## [64] 1066.1903 1059.8952 1162.7587 1235.9274 1225.3796 1301.3378 1367.9983
## [71] 1536.6519 1742.0344 1854.3053 1672.5129 1641.0145 1600.9557 1888.2666
## [78] 1870.9247 1774.0627 2187.6086 2590.0406 2591.4637 2759.9756 2491.9945
## [85] 2673.8482 2557.7268 2551.3342 2374.5391 1942.3399 1709.0153 1881.5158
## [92] 1663.7164 1585.9097 1458.6386 1454.9041 1562.3943 1528.3993 1405.2286
## [99] 1509.1027 1544.7643
Одредимо основне дескриптивне статистике на основу симулација.
## [1] 1000.000 1024.193 1042.864 1055.457 1072.159 1090.415 1109.529 1130.774
## [9] 1153.229 1170.192
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## 10% 1000 895.7908 863.7127 841.4886 816.9554 810.9626 801.2245 785.6669
## 90% 1000 1158.0943 1235.2097 1289.0765 1351.1922 1406.4950 1452.1362 1523.6120
## [,9] [,10]
## 10% 770.3563 776.1377
## 90% 1584.2758 1642.5945
plot(N_mean, type = "l", ylim = c(0, 10000))
lines(N_quants[1,], lty = 2)
lines(N_quants[2,], lty = 2)Да бисмо одредили фреквенције појављивања јединки можемо користити
функцију table. Нпр. занима нас колико итерација је у
десетој години било мање од 1000 јединки.
## out10
## greater10 less10
## 636 364
Случајно лутање
Једнодимензионо случајно лутање: Честица се креће по правој, полазећи из тачке 0 и прави јединични корак у позитивном смеру са вероватночом \(p\), а јединични корак у негативном смеру са вероватночом \(1-p\). Кораци у лутању су дати низом независних случајних величина \(\{X_k, \, k\in\mathbb{N}\}\), за који важи \[P\{X_k = 1\} = p, \,\, P\{X_k = -1\} = 1- p.\] Случајни низ \(\{S_n, n\in\mathbb{N}_0\}\), чији је општи члан дефинисан са: \[S_0 = 0, \,\, S_n = X_1+\dots+X_n, \, ,\ge 1,\] представља положај честице после \(n\) корака.
Нека је \(T\) случајна величина која представља тренутак када се честица први пут врати у нулу, тј.
\[T = \min\{n\in\mathbb{N}_+:\, X_n = 0\}.\] Приметимо да је повратак у нулу могућ само након парног броја корака, те случајна величина \(T\) узима вредности \(\{2,4,\dots\} \cup\{+\infty\}\).
Функција расподеле случајне величине \(T_{2n}\) дата је са \[\mathbb{P}\{T = 2 n\} = \binom{2 n}{n} \frac{1}{2 n - 1} p^n (1 - p)^n, \quad n \in \mathbb{N}_+.\]
Пример: Ана и Тамара бацају фер новчић. Ана добија бод ако падне глава, а Тамара добија бод ако падне писмо. Одредити вероватночу да ће имати исти број поена први пут након \(n\) бацања новчића, за свако \(n\in\{2,4,6,8,10\}\). (решење: \(f(2) = \frac{1}{2}\), \(f(4) = \frac{1}{8}\), \(f(6) = \frac{1}{16}\), \(f(8) = \frac{5}{128}\), \(f(10) = \frac{7}{512}\))
simulate_equal_scores <- function(n, trials = 100000) {
count <- 0
for (trial in 1:trials) {
tosses <- sample(c("H", "T"), size = n, replace = TRUE)
heads <- cumsum(tosses == "H")
tails <- cumsum(tosses == "T")
if (heads[n] == tails[n] && all(heads[-n] != tails[-n])) {
count <- count + 1
}
}
return(count / trials)
}
n_values <- c(2, 4, 6, 8, 10)
set.seed(123)
simulated_probabilities <- sapply(n_values, simulate_equal_scores)
names(simulated_probabilities) <- n_values
print(simulated_probabilities)## 2 4 6 8 10
## 0.50196 0.12507 0.06229 0.03848 0.02801
Stock Market
Акцијама компаније CountBayes тргује се на берзи под ознаком BAYZ. У просеку акција добија 1.001 пута већу вредност од своје почетне цене током једног дана, али то може варирати са стандардном девијацијом од 0.005 на било који дан (волатилност). Можемо симулирати један узорак путање за BAYZ акције тако што ћемо израчунати кумулативни производ из нормалне расподеле са средњом вредношћу од 1.001 и стандардном девијацијом од 0.005. Под претпоставком да BAYZ акције имају почетну цену 20 долара по акцији, илуструјемо пример путање акција за 200 дана.
days <- 200
changes <- rnorm(200, mean = 1.001, sd = 0.005)
plot(cumprod(c(20,changes)), type='l', ylab="Price", xlab="day", main="BAYZ closing price (sample path)")Али ово је само једна могућа реализација кретања акција! Ако размишљате о инвестирању у BAYZ акције, желите да знате које су могуће цене акције на крају 200. дана. Да бисте проценили ризик ове акције, морате знати који су интервали поверења горње и доње границе будуће цене.
Да бисмо решили ово, симулираћемо 100.000 различитих могућих путања које акција може узети и затим ћемо анализирати расподелу цена за последњи дан.
runs <- 100000
generate.path <- function(){
days <- 200
changes <- rnorm(200,mean=1.001,sd=0.005)
sample.path <- cumprod(c(20,changes))
closing.price <- sample.path[days+1] # +1 because we add the opening price
return(closing.price)
}
mc.closing <- replicate(runs,generate.path())
quantile(mc.closing, 0.95)## 95%
## 27.38742
## 5%
## 21.69453
Напомена: Ово је поједностављен модел кретања акција на берзи. Чак и модели који се генерално сматрају слабим моделима за цене акција користе бар логнормалну расподелу… У реалним сценаријима, квантитативне финансије интензивно користе Монте Карло симулације.
Games of chance
У игри “Games of chance” играч осваја 1 бод ако се стрелица заустави на жутој боји, губи 1 бод ако се заустави на црвеној и добија 2 бода ако се заустави на плавој боји.
Лако налазимо очекивани добитак играча: \[E(X) = \frac{1}{2}\cdot 1 + \frac{1}{4}\cdot (-1) + \frac{1}{4}\cdot 2 = 0.75.\]
Колика је вероватноћа да играч има мање од 0 поена након што казаљку заврти 10 пута?
runs <- 100000
#simulates on game of 10 spins, returns whether the sum of all the spins is < 1
play.game <- function(){
results <- sample(c(1,1,-1,2), 10, replace=T)
return(sum(results) < 0)
}
mc.prob <- sum(replicate(runs,play.game()))/runs
mc.prob## [1] 0.01388
ViS лото игра
Да би освојио одређену лутрију, играч мора саставити реч “ViS”. \(60\%\) листића садржи слово “V”, \(30\%\) садржи слово “i”, а \(10\%\) садржи слово “S”. Одредити просечан број листића које особа мора купити да би освојила награду. (30 пута поновити експеримент у циљу доношења закључка)
Свако слово има дату вероватноћу да се нађе на листићу и то редом:
- “V” -> 0.6,
- “i” -> 0.3,
- “S” -> 0.1.
Успоставимо везу између датих вероватноћа и (псеудо)случајних бројева. Сваком од слова доделићемо бројеве који ће бити резултат случајног генератора и то:
- “V” -> 0,1,2,3,4,5,
- “i” -> 6,7,8,
- “S” -> 9.
Дефинишимо функцију која симулира једну реализацију лото игре.
# Create a function to get result
Random_Number_List <- c() # Create an empty vector to append the result
Lottery <- function(){
V <- FALSE # Set default False for all outcome
i <- FALSE # Set default False for all outcome
S <- FALSE # Set default False for all outcome
Total_Pick <- 0 # Number of Pick is 0
while(!V | !i | !S){ # Only stop when temporary values of V, i, S holing TRUE at the same time
Total_Pick <- Total_Pick + 1 # Count the number of Pick
Random_Number <- sample(0:9,1) # Get random numbers from 0 to 9 with 1 unit space
Random_Number_List <- append(Random_Number_List, Random_Number) # Record the random number
Random_Number_List <- paste(Random_Number_List, collapse = ", ") # Convert to character to illustrate the consequence of number
if(Random_Number<=5){V <- TRUE} # Assign value to random number for V
else if(Random_Number<=8){i <- TRUE} # Assign value to random number for i
else {S <- TRUE} # Assign a value to random number for S
Result <- data.frame(Random_Number_List=Random_Number_List,Total_Pick=Total_Pick) # Data frame the result
}
return(Result)
}
Lottery()## Random_Number_List Total_Pick
## 1 5, 4, 8, 4, 8, 4, 0, 0, 9 9
df <- data.frame(Random_Number_List = character(),
Total_Pick = integer(),
stringsAsFactors = FALSE) # Empty data frame to record result of each trial
Experiment <- function(n){ # Function with input n
trial <- c(1:n) # Vector of trial from 1 to n
for( k in trial){
if(k<=n){ # Repeat calling function n times
Random_Number_List <- c() # Clear the list from last record
Result <- Lottery() # Run Lottery function
}
df <- rbind(df,Result) # Append result for each trial
Sum_Trial <- sum(df$Total_Pick) # Sum of picks of total trial
Average_Pick <- Sum_Trial/n # Average pick
}
return( list( data = df, # Create a list of return
Sum_Trial = Sum_Trial,
Average_Pick = Average_Pick))
}## [1] 11.6
## [1] 11.009
## [1] 11.134
Game Mathematician Test - “Pick A Prize” Game
Game rules: Players are presented with num_options hidden options. One of the hidden options is “collect”, and the others are credit prizes. Players will pick (and that way, reveal) options one by one until they pick the “collect” option. When they pick the “collect” option, all revealed credit prizes are awarded to the player and the game ends. Every credit prize is predetermined before the first pick and is chosen independently with a new random draw. Whenever a player picks a credit prize, and there are more hidden credit prizes on the screen (at least 2 hidden options left), there is a chance for a “bonus” feature. Othervise, the “bonus” feature is not available. The “bonus” feature reveals one more hidden credit prize automatically. After the “bonus” credit prize, the players continue picking, and the “bonus” feature is still available in the following picks.
Inputs you will need:
- num_options – number of hidden options. Example: num_options = 15
- award_values – possible award values. Example: award_values = [10, 20, 50, 100]
- award_values_probabilities – probabilities for choosing a value from award_values. Example: [0.5, 0.25, 0.2, 0.05]
- prob_bonus – the odds of activating a bonus award after a credit prize pick. Example: prob_bonus = 0.5
Implement a function single_game() that simulates a single game. This function should return the number of revealed awards and the awarded credit prize in a single game play.
Call a function simulate(num_games) which plays single_game(), num_games amount of times. It should return the average number of revealed awards and the average awarded credit prize. What are the results for the example inputs?
single_game <- function(num_options, award_values, award_values_probabilities, prob_bonus) {
revealed_awards <- 0
total_credit_award <- 0
hidden_options <- c("collect", sample(award_values, num_options - 1, replace = TRUE, prob = award_values_probabilities))
hidden_options <- sample(hidden_options) # Shuffle
while (length(hidden_options) > 0) {
# Player picks an option
index <- sample(length(hidden_options), 1)
pick <- hidden_options[index]
hidden_options <- hidden_options[-index]
revealed_awards <- revealed_awards + 1
# If option is "collect", the game ends
if (pick == "collect") {
break
}
total_credit_award <- total_credit_award + as.numeric(pick)
# Check if bonus can activate
if (length(hidden_options) >= 2 && runif(1) < prob_bonus) {
# Bonus reveals one more hidden credit prize, it cannot reveal collect
hidden_options_bonus <- hidden_options[hidden_options != "collect"]
if (length(hidden_options_bonus) > 0) {
index <- sample(length(hidden_options_bonus), 1)
bonus_pick <- hidden_options_bonus[index]
revealed_awards <- revealed_awards + 1
total_credit_award <- total_credit_award + as.numeric(bonus_pick)
}
}
}
return(list(revealed_awards = revealed_awards, total_credit_award = total_credit_award))
}
single_game(15, c(10, 20, 50, 100), c(0.5, 0.25, 0.2, 0.05), 0.5)## $revealed_awards
## [1] 4
##
## $total_credit_award
## [1] 120
simulate <- function(num_games) {
num_options <- 15
award_values <- c(10, 20, 50, 100)
award_values_probabilities <- c(0.5, 0.25, 0.2, 0.05)
prob_bonus <- 0.5
total_revealed_awards <- 0
total_credit_prizes <- 0
# Simulate single game num_games times
for (i in 1:num_games) {
game_result <- single_game(num_options, award_values, award_values_probabilities, prob_bonus)
total_revealed_awards <- total_revealed_awards + game_result$revealed_awards
total_credit_prizes <- total_credit_prizes + game_result$total_credit_award
}
avg_revealed_awards <- total_revealed_awards / num_games
avg_credit_prize <- total_credit_prizes / num_games
return(list(avg_revealed_awards = avg_revealed_awards, avg_credit_prize = avg_credit_prize))
}
simulate(10000)## $avg_revealed_awards
## [1] 11.5929
##
## $avg_credit_prize
## [1] 265.592