You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R语言中从自定义分布执行蒙特卡洛模拟抽样

Sampling from Your Custom Distribution in R via Monte Carlo

Got it, let's break this down step by step. First, let's clarify what your custom distribution actually is: you've defined an unnormalized probability density function (PDF) with this line:

points <- Rmax * dexGAUS(x, mu = mu, sigma = sigma, nu = tau) * pgamma(x, shape = kappa, rate = rate)

This is a scaled product of the exponentially modified Gaussian (exGAUS) density and the Gamma cumulative distribution function (CDF), restricted to x ∈ [1, 20]. The key issue here is that points isn't a valid PDF yet—its integral over [1,20] doesn't equal 1. That's almost certainly why your first sampling attempt gave a histogram that didn't match expectations.

Below are two robust Monte Carlo methods to sample from this distribution, with code examples and explanations.

Method 1: Accept-Reject Sampling (Intuitive & Efficient for Simple Supports)

Accept-Reject is a great starting point for distributions with a finite support (like your [1,20] range). Here's how to implement it:

Step 1: Formalize the Unnormalized PDF

First, wrap your distribution in a reusable function:

library(gamlss)

# Your original parameters
mu <- 1
sigma <- 2
tau <- 3
kappa <- 3
rate <- 1
Rmax <- 20

# Define the unnormalized PDF
f <- function(x) {
  Rmax * dexGAUS(x, mu = mu, sigma = sigma, nu = tau) * pgamma(x, shape = kappa, rate = rate)
}

Step 2: Set Up the Proposal Distribution

We'll start with a simple uniform proposal over [1,20] (easy to implement). Later, we'll show a more efficient proposal using the exGAUS distribution (since your target has an exGAUS component).

First, calculate the maximum value of f(x) (needed for the acceptance criterion):

x_grid <- seq(1, 20, 0.01)
f_max <- max(f(x_grid))

Step 3: Implement the Accept-Reject Sampler

accept_reject_uniform <- function(n_samples) {
  samples <- numeric(n_samples)
  accepted <- 0
  
  while (accepted < n_samples) {
    # Draw a candidate from the uniform proposal
    x_candidate <- runif(1, min = 1, max = 20)
    # Calculate acceptance probability (uniform PDF = 1/(20-1))
    accept_prob <- f(x_candidate) / (f_max * (1/19))
    # Draw a uniform random number to decide acceptance
    u <- runif(1)
    
    if (u <= accept_prob) {
      accepted <- accepted + 1
      samples[accepted] <- x_candidate
    }
  }
  return(samples)
}

# Generate 1000 samples (set seed for reproducibility)
set.seed(123)
samples_uniform <- accept_reject_uniform(1000)

Step 4: Validate the Samples

Compare the histogram to the normalized target PDF (divide f(x) by its integral over [1,20]):

# Calculate normalization constant
norm_const <- integrate(f, lower = 1, upper = 20)$value

# Plot
hist(samples_uniform, freq = FALSE, main = "Samples vs Target PDF", 
     xlab = "x", col = "lightgray")
lines(x_grid, f(x_grid)/norm_const, col = "red", lwd = 2)

Boost Efficiency with a Better Proposal

The uniform proposal works but can have high rejection rates. Instead, use a truncated exGAUS distribution (matches the core of your target distribution):

# Truncated exGAUS proposal PDF
g <- function(x) {
  exgaus_cdf_range <- pexGAUS(20, mu = mu, sigma = sigma, nu = tau) - pexGAUS(1, mu = mu, sigma = sigma, nu = tau)
  dexGAUS(x, mu = mu, sigma = sigma, nu = tau) / exgaus_cdf_range
}

# Calculate max ratio of target to proposal
ratio <- f(x_grid)/g(x_grid)
M <- max(ratio)

# Efficient accept-reject sampler
accept_reject_exgaus <- function(n_samples) {
  samples <- numeric(n_samples)
  accepted <- 0
  exgaus_cdf_range <- pexGAUS(20, mu = mu, sigma = sigma, nu = tau) - pexGAUS(1, mu = mu, sigma = sigma, nu = tau)
  
  while (accepted < n_samples) {
    # Draw candidate from truncated exGAUS
    x_candidate <- rexGAUS(1, mu = mu, sigma = sigma, nu = tau)
    while (x_candidate < 1 || x_candidate > 20) {
      x_candidate <- rexGAUS(1, mu = mu, sigma = sigma, nu = tau)
    }
    # Acceptance probability
    accept_prob <- f(x_candidate)/(M * g(x_candidate))
    u <- runif(1)
    
    if (u <= accept_prob) {
      accepted <- accepted + 1
      samples[accepted] <- x_candidate
    }
  }
  return(samples)
}

set.seed(123)
samples_exgaus <- accept_reject_exgaus(1000)

Method 2: Metropolis-Hastings Sampling (Great for Complex Distributions)

If accept-reject feels too slow, the Metropolis-Hastings algorithm (a type of MCMC) is a solid alternative. It uses a Markov chain to converge to your target distribution:

mh_sampler <- function(n_samples, init_val = 10, burn_in = 1000) {
  samples <- numeric(n_samples + burn_in)
  current <- init_val
  
  for (i in 1:(n_samples + burn_in)) {
    # Propose a new value from a normal distribution centered at current
    candidate <- rnorm(1, mean = current, sd = 2)
    # Reject candidates outside [1,20]
    if (candidate < 1 || candidate > 20) {
      samples[i] <- current
      next
    }
    # Calculate acceptance probability (symmetric proposal, so ratio simplifies)
    accept_prob <- min(1, f(candidate)/f(current))
    u <- runif(1)
    
    if (u <= accept_prob) {
      current <- candidate
    }
    samples[i] <- current
  }
  # Discard burn-in samples (chain needs time to converge to target)
  return(samples[(burn_in + 1):(burn_in + n_samples)])
}

set.seed(123)
samples_mh <- mh_sampler(1000)

# Validate
hist(samples_mh, freq = FALSE, main = "MH Samples vs Target PDF", 
     xlab = "x", col = "lightblue")
lines(x_grid, f(x_grid)/norm_const, col = "darkblue", lwd = 2)

Why Your First Attempt Failed

Chances are you tried to sample directly from the exGAUS distribution and then modify it with pgamma—but pgamma is a CDF, not a valid transformation for generating random variables. You need to treat your custom expression as an unnormalized PDF and use proper Monte Carlo techniques to sample from it.


内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:20:12