Processing math: 100%

TD 2 : corrigé des exercices 2 et 3

EXERCICE 2 : Rejet et loi de Laplace

2.1.

voir corrigé TD2 (13/03/20) : proposition de generateur de laplace (methode d'inversion)

code :

In [1]:
# generateur de la loi de laplace
rand_laplace <- function(n){
  # par inversion
  ru = runif(n)
  return( log(2*ru)*(ru<1/2) + -log(2*(1-ru))*(ru>=1/2) )
}
# densite de la loi de Laplace
density_laplace <- function(x){
  return( (1/2) * exp(-abs(x)) )
}

generons un echantillon de taille n = 20 000

In [2]:
n = 20000
sample = rand_laplace(n)

verifions que l'histogramme et la densite se superposent bien

In [7]:
hist(sample, prob = TRUE, col = "steelblue",
     main = "densité de la loi de Laplace",
    xlab = "échantillon",
    ylab = "densité")
x = seq(-8, 5, 0.01)
lines(x, density_laplace(x), col = "red", lwd = 2)

2.2.

voir corrigé TD2 (13/03/20): proposition de generateur de la loi normale (methode de rejet-acceptation)

code :

In [8]:
# generateur de la loi gaussienne a l'aide de
# l'algorithme rejet-acceptation et du generateur de la loi de Laplace
rand_gauss <- function(n){
  # ar method
  m = sqrt((2*exp(1))/pi)
  rn = rep(0, n)
  total = 0
  for(i in 1:n){
    rn[i] = rand_laplace(1)
    total = total + 1
    while( dnorm(rn[i]) < density_laplace(rn[i])*m*runif(1) ){
      rn[i] = rand_laplace(1)
      total = total + 1
    }
  }
  return(list("sample" = rn, "n_loop" = total))
}

générons un échantillon de taille n=20000

In [10]:
n = 20000
sample_norm = rand_gauss(n)

vérifions que l'histogramme et la densité se superposent bien

In [12]:
hist(sample_norm$sample, prob = TRUE, col = "steelblue",
     main = "densité de la loi gaussienne",
    xlab = "échantillon",
    ylab = "densité")
x = seq(-10, 10, 0.01)
lines(x, dnorm(x), col = "red", lwd = 2)

2.3.

In [14]:
rate_1 = length(sample_norm$sample) / sample_norm$n_loop
rate_1
0.753835136255701

le taux d'acceptation de l'algorithme est de rate_1 = 0.76.

Voici une proposition de simplification de la condition d'acceptation :

In [15]:
rand_gauss_opt <- function(n){
  # methode acceptation-rejet
  rr = 2*round(runif(n))-1 # sampling n Rademacher
  rn = rep(0,n)
  total = 0
  for(i in 1:n){
    y = rexp(1)
    total = total + 1
    while( exp(-(y-1)^2/2) <= runif(1) ){
      y = rexp(1)
      total = total + 1
    }
    rn[i] = rr[i]*y
  }
  return(list("sample" = rn, "n_loop" = total))
}

Regardons si la densité et de l'histogramme représentant l'échantillon généré se superposent bien

In [18]:
sample_norm_opt = rand_gauss_opt(20000)
hist(sample_norm_opt$sample, prob = TRUE, col = "steelblue",
     main = "densité de la loi gaussienne",
    xlab = "échantillon",
    ylab = "densité")
lines(x, dnorm(x), col = "red", lwd = 2)

comparons le temps de calcul entre les deux fonctions suivantes :

  • rand_gauss : générateur de la loi gaussienne par l'algorithme acceptation-rejet
  • rand_gauss_opt : générateur de la loi gaussienne par l'algorithme acceptation-rejet simplifié
In [20]:
n = 800000

t0=proc.time()
sample_norm = rand_gauss(n)
t_norm = proc.time()-t0

t1=proc.time()
sample_norm_opt = rand_gauss_opt(n)
t_norm_opt = proc.time()-t1

print(paste0("l'algorithme 'classique' nous donne ",t_norm["elapsed"], " sec"))
print(paste0("l'algorithme 'optimisé' nous donne ",t_norm_opt["elapsed"], " sec"))
[1] "l'algorithme 'classique' nous donne 8.57299999999987 sec"
[1] "l'algorithme 'optimisé' nous donne 5.6239999999998 sec"

Notons que la fonction rand_gauss_opt est plus rapide que la fonction rand_gauss

2.4

Posons pour tout x,

r(x)=p(x)f(x)=π2e|x|+x2/2

Nous avons quand x+:

r(x)+

Il n'existe donc pas de constante M qui puisse majorer r. Ainsi, nous ne pouvons pas générer la loi de Laplace via la loi normale à l'aide de l'algorithme rejet-acceptation

EXERCICE 3 : Box-Muller amélioré

Dans la suite nous appelons :

  • BM pour la méthode Box-Muller "classique" (la version cartésienne)
  • BMA pour la méthode Box-Muller "amélioré" (la version polaire)

3.1.

Voir corrigé TD2

3.2.

Bien que BMA rejette certains points (1π4, ce qui correspond à env. 20%), l'algorithme BM requiert des calculs trigonométriques (calcul de sin et cos). Ces fonctions trigonométriques ont des désavantages non négligeables :

  1. elles sont coûteuses en temps de calcul

BM et BMA ont approximativement le même temps de calcul

  1. et leur programme informatique (fonction sous R, python, matlab ...) n'est qu'approximation des "vrais" fonctions trigonométriques

à la place BMA propose une simple division, ce qui rend le code plus robuste

In [124]:
rand_gauss_BM <- function(n){
    # generateur de n gaussiens par BM standard
    rn <- rep(0,n)
    for(i in seq(1, n, by=2)){
        ru <- runif(1)
        rv <- runif(1)
        rn[i] <- sqrt(-2*log(ru)) * sin(2*pi*rv)
        rn[i+1] <- sqrt(-2*log(ru)) * cos(2*pi*rv)
    }
    return(rn)
}
In [125]:
rand_gauss_BMA <- function(n){
    # generateur de n gaussiens par BM "ameliore"
    rn <- rep(0,n)
    for(i in seq(1, n, by=2)){
      ru1 <- runif(1,-1,1)
      ru2 <- runif(1,-1,1)
      while(ru1**2 + ru2**2 > 1){
        ru1 <- runif(1,-1,1)
        ru2 <- runif(1,-1,1)
      }
      s <- ru1**2 + ru2**2
      rn[i] <- ru1*sqrt(-2*log(s)/s)
      rn[i+1] <- ru2*sqrt(-2*log(s)/s)
    }
    return(rn)
}
In [126]:
n = 20000

t0=proc.time()
sample_norm_BM <- rand_gauss_BMA(n)
t_norm = proc.time()-t0

t1=proc.time()
sample_norm_BMA <- BMPolaire(n)
t_norm_opt = proc.time()-t1

print(paste0("temps machine pour l'algorithme 'classique' : ",t_norm["elapsed"], " sec"))
print(paste0("temps machine pour l'algorithme 'optimisé' : ",t_norm_opt["elapsed"], " sec"))
[1] "temps machine pour l'algorithme 'classique' : 0.129000000000815 sec"
[1] "temps machine pour l'algorithme 'optimisé' : 0.165999999997439 sec"
In [127]:
# graphique
hist(sample_norm_BM, prob = TRUE, col = "steelblue",
     main = "densité des deux distributions",
     xlab = "echantillons",
     ylab = "densite")
hist(sample_norm_BMA, prob = TRUE, col = rgb(1,0,0,0.4), add=T)
x = seq(-10, 10, 0.01)
lines(x, dnorm(x), col = "orange", lwd = 2)
legend("topright", c("BM classique", "BM amelioré", "vrai densité"), col=c("steelblue", "red", "orange"), lwd=6)

Rermarque importante : l'implémentation suivante permet de construire un générateur beaucoup plus rapide que les implémentations précédentes.

Exercice : Pourquoi ?

In [128]:
rand_gauss_BM_bis <- function(n){
  # generateur de n gaussiens par BM standard
  ru = runif(n/2)
  rv = runif(n/2)
  rn = c(sqrt(-2*log(ru)) * sin(2*pi*rv), sqrt(-2*log(ru)) * cos(2*pi*rv))
  return(rn)
}
In [129]:
n = 100000

# générateur BM version cartésiennes
t0=proc.time()
sample_norm_BM <- rand_gauss_BM(n)
t_norm = proc.time()-t0

# générateur BM  version cartésiennes avec le programme modifié
t0_bis=proc.time()
sample_norm_BM <- rand_gauss_BM_bis(n)
t_norm_bis = proc.time()-t0_bis

# générateur BM version polaire
t1=proc.time()
sample_norm_BMA <- rand_gauss_BMA(n)
t_norm_opt = proc.time()-t1

print(paste0("temps machine pour BM : ",t_norm["elapsed"], " sec"))
print(paste0("temps machine pour BM_bis : ",t_norm_bis["elapsed"], " sec"))
print(paste0("temps machine pour BMA : ",t_norm_opt["elapsed"], " sec"))
[1] "temps machine pour BM : 0.31000000000131 sec"
[1] "temps machine pour BM_bis : 0.00899999999819556 sec"
[1] "temps machine pour BMA : 0.347000000001572 sec"