El rechazo de muestreo es muy simple aquí (ver la aceptación o de respuesta al final de este post para alternativas más eficientes, a pesar de que):
- Construir un rectángulo alrededor de la densidad (que es fácil aquí, debido a la limitada apoyo) de la caja verde en la figura.
- Dibujar uniforme de variables en el cuadro (por lo que sólo dos uniformes con los vectores de la derecha apoye en ambas direcciones) - todos los puntos de la figura, tanto en blanco y rojo.
- Mantener los puntos que están por debajo de la densidad de la doble triangular (los puntos rojos en la figura).
- Tomar el "$x$-eje" de las coordenadas de estos puntos como realizaciones de la doble triangular.
En una imagen:
Creado con
a <- 0
b <- 2
c <- 1
lambda <- .5
x <- seq(a,b,by=.01)
ddoubletriangular <- function(x,a,b,c,lambda){
p <- lambda/2*(c-a)^2
mu <- 2*(1-p)/(b-c)^2
return(lambda*(x-a)*(x<=c)+mu*(b-x)*(x>c))
}
plot(x,ddoubletriangular(x,a,b,c,lambda),type="l")
upperbound <- max(ddoubletriangular(x,a,b,c,lambda))
lines(c(b,b,a,a,b),c(0,upperbound,upperbound,0,0),col="green",lwd=4)
px <- runif(100,min=a,max=b)
py <- runif(100,min=0,max=upperbound)
points(px,py)
ikeep <- (py < ddoubletriangular(px,a,b,c,lambda))
px <- px[ikeep]
py <- py[ikeep]
points(px,py,col="red",pch=19)
Vamos a tratar más que $N=100$ sorteos para el estudio de la calidad del algoritmo:
N <- 1500000
px <- runif(N,min=a,max=b)
py <- runif(N,min=0,max=upperbound)
ikeep <- (py < ddoubletriangular(px,a,b,c,lambda))
px <- px[ikeep]
truehist(px,main="Rejection method",sub=paste("n=",length(px),"of",N))
lines(x,ddoubletriangular(x,a,b,c,lambda),type="l")
Esto se traduce en
ACTUALIZACIÓN:
Fuerza documentado en los comentarios, el algoritmo anterior es, "fuera de la plataforma", innecesariamente ineficiente. Aquí es una ilustración de lo que @whuber propone (o eso espero):
Primera mejora:
La construcción de dos cajas rectangulares y realizar rechazo de muestreo en cada región por separado (con una probabilidad de $p$ para el primer segmento, y $1-p$ para el segundo), ver la siguiente figura. Esto mediante la construcción mejora la probabilidad de aceptación a 1/2.
Segunda mejora:
Reconocer que las regiones en las casillas de cada uno son lo @whuber llamadas "rectangular simétrica": el triángulo situado encima de un segmento de la densidad lineal es una versión rotada de la aceptación triángulo debajo de la densidad. Por lo tanto, podemos usar el "rechazado" muestras del algoritmo básico y el uso de estas como legítimo realizaciones para el otro extremo del segmento.
Por ejemplo, el rechazo de un sorteo con $x$-coordinar $c+\epsilon$ puede ser utilizado como un aceptados sorteo de $b-\epsilon$, debido a la "altura" de la región de rechazo en $c+\epsilon$ es por la construcción idéntica a la "altura" de la aceptación de la región en $b-\epsilon$. Esto tiene la interesante característica de aumento de la tasa de aceptación del 100%.
Aquí es una forma de código:
a <- 0
b <- 2
c <- 1
lambda <- .5
x <- seq(a,b,by=.002)
p <- lambda/2*(c-a)^2
mu <- 2*(1-p)/(b-c)^2
ddoubletriangular <- lambda*(x-a)*(x<=c)+mu*(b-x)*(x>c)
upperbound1 <- lambda*c
upperbound2 <- mu*x[min(which(x>c))+1]
N <- 1500000
px1 <- runif(N*p,min=a,max=c)
py1 <- runif(N*p,min=0,max=upperbound1)
px2 <- runif(N*(1-p),min=c,max=b)
py2 <- runif(N*(1-p),min=0,max=upperbound2)
ikeep1 <- (py1 < lambda*(px1-a))
px1a <- px1[ikeep1]
px1b <- a+c-px1[!ikeep1]
ikeep2 <- (py2 < mu*(b-px2))
px2a <- px2[ikeep2]
px2b <- c+b-px2[!ikeep2]
px <- c(px1a,px1b,px2a,px2b)
truehist(px,main="Modified rejection method",sub=paste("n=",length(px),"of",N))
lines(x,lambda*(x-a)*(x<=c)+mu*(b-x)*(x>c),type="l")