とーけい部

現在、このブログは工事中です。

統計学 統計検定 数学

Quasiモンテカルロお試し:(超一様分布列による積分計算)

超一様分布列(LDS)と積分

超一様分布列というのは、凄い一様に分布してる列のことです(は?)。
これは英語で言うとLow-Descrepancy Sequence なので、略してLDSと呼ばれることもあるようです。

例えば、1/2 , 1/4 , 3/4 , 1/8 , 5/8 , 3/8 , ……
というような数列を第100項まで図示すると、以下のようになります。
f:id:hasegawa-yuta289:20210605231708p:plain

これに対して、ただ単に一様乱数を100個とっただけの数列を図示すると、以下のようになります。
f:id:hasegawa-yuta289:20210605231813p:plain

比べてみれば一目瞭然ですね、超一様分布列(LDS)の方が、ただの一様分布の列よりも、わざとらしくもっと"一様に"分布しているのがわかります。

こういった超一様分布列を利用することで、積分計算を通常のモンテカルロよりも早く終わらせることができる、可能性があります。便利ですねえ~
LDSを利用したモンテカルロ計算(いやまあ、もはやモンテカルロ計算ではないんですけど)のことを、Quasi -モンテカルロと呼ぶのですが、QMCと略すことにしましょう。
Quasiとは、"疑似"という意味らしいので、日本語訳すると疑似モンテカルロということになりますけど……超一様分布列は「決定論的なただの数列」なので、これを使った積分モンテカルロ計算と呼ぶとややこしいですね。モンテカルロ法は通常は(疑似)乱数からの積分計算を指しますからね。まあしょうがないですね、そういう用語なんで。
(追記:Quasi-モンテカルロの日本語訳は準モンテカルロらしいです。)

というわけで、今回はLDSを利用した積分をちょっとやってみましょう。

積分をやってみよう!

以下のような関数hを考えます。

\displaystyle{
\Large{
\begin{eqnarray}
f(x) &=& sin(10x)arctan(10x)^2 \\
g(y) &=& -cos(2\pi y)|log(\Gamma(3y))|^ {arcsin(y)} \\
h(x,y) &=& (f(x)+g(y))((x+y)/2+1/2)
\end{eqnarray}
}
}

関数hは、0<x<1 , 0<y<1 の範囲について、以下のような関数になっています。
(なんかうまくgif作れなくて、長時間眺めてると気持ち悪くなってきそうな図になりました。申し訳ない)

f:id:hasegawa-yuta289:20210605233104g:plain

これについて、0<x<1 , 0<y<1 での積分をやってみましょう。

超一様分布列には、Halton列を利用します。
計算結果は以下のようになりました。

f:id:hasegawa-yuta289:20210605233451p:plain

一様分布を利用した通常のモンテカルロよりも、Quasiモンテカルロの方が収束が速そうに見えますね。

今回の記事を再現するためのコードを、以下に置いておくので、試しにRで動かしてみてくださ~い。(おわり)

コード

## 設定
f <- function(x){
  a <- sin(10*x) * atan(10*x)^2
  return(a)
}
curve(f , xlim=c(-1.2,1.2),col="red",lwd=2)
abline(h=0,lwd=2)

g <- function(y){
  b <- -cos(2*pi*y) * abs(lgamma(3*y))^asin(y)
  return(b)
}
curve(g,col="blue",lwd=2)
abline(h=0,lwd=2)

h <- function(x,y){
  c <- (f(x)+g(y))*((x+y)/2+1/2)
  return(c)
}

x <- y <- seq(0,1,length=50)
z <- outer(x,y,FUN=h)
persp(x=x,y=y,z=z,
      theta = -40,
      phi   = 20)

## 超一様分布列の利用

# 2,3進数変換  
trans2 <- function(n){
  if(n <= 0){
    return(NULL)
  }else{
    return(append(Recall(n%/%2), n%%2))
  }
}
trans2(1)

trans3 <- function(n){
  if(n <= 0){
    return(NULL)
  }else{
    return(append(Recall(n%/%3), n%%3))
  }
}

## Halton列の作成  
H2 <- function(N){
  a <- numeric()
  for(i in 1:N){
    b <- rev(trans2(i))
    a[i] <- sum(2^(-1*(1:length(b))) *b) 
  }
  return(a)
}

plot(H2(100),main="超一様分布列")
plot(runif(100) , main="ただの一様分布列")

H3 <- function(N){
  a <- numeric()
  for(i in 1:N){
    b <- rev(trans3(i))
    a[i] <- sum(3^(-1*(1:length(b))) *b) 
  }
  return(a)
}


plot(runif(1000),runif(1000),main="(疑似)乱数列")
plot(H2(1000) , H3(1000) , main="Halton列")

N <- 10000
res1 <- numeric()
res2 <- numeric()
X <- runif(N)
Y <- runif(N)
for(i in 1:N){
  res1[i] <- mean(h(X[1:i] , Y[1:i]))
}
plot(res1,type="l")

X <- H2(N)
Y <- H3(N)
for(i in 1:N){
  res2[i] <- mean(h(X[1:i] , Y[1:i]))
}
plot(res2,type="l")

plot(res1,type="l",col="red",ylim=c(-0.4,0.4),main="通常のモンテカルロとQuasiモンテカルロの比較")
lines(res2,col=adjustcolor(alpha=0.7,col="blue"))
legend("topright",col=c("red","blue"),legend=c("MC","QMC"),lty=1,lwd=2,cex=1.5)

参考文献