The following R function performs inverse transform sampling with vectorized numerical computation. It checks finite real scalar parameters, including a nonnegative integer sample count, and gives beta its requested default.
r_sample_f <- function(n, p, beta = 1) {
  scalar <- function(v) {
    is.numeric(v) && !is.complex(v) && length(v) == 1L &&
      is.finite(v)
  }
  if (!scalar(n) || n < 0 || n != floor(n))
    stop("n must be a nonnegative integer")
  if (!scalar(p) || p <= 0 || p >= 1)
    stop("p must lie strictly between 0 and 1")
  if (!scalar(beta) || beta <= 0)
    stop("beta must be positive")
  if (n == 0) return(numeric(0))
  v <- runif(n)
  (log1p(-p) - log(-expm1(v * log(p)))) / beta
}
Here is again a uniform distribution draw, so the expression is the quantile function from (ii). The function expm1(t) evaluates accurately when is small, avoiding cancellation in ; log1p(-p) evaluates . For default bounds, runif excludes both endpoints, so this implementation does not deliberately generate the infinite endpoint of the quantile function. It returns a numeric vector of length n, including an empty vector when n = 0. [ref] [ref]
Let and , evaluated from the given polylogarithm formulas. Generate a large sample of independent and identically distributed random variables with r_sample_f, and compute its sample mean and sample variance using mean and var. The central limit theorem gives the first standardized check:
For the second check, the correct asymptotic variance of the sample variance involves the fourth central moment :
The denominator is not unless the probability distribution has the requisite fourth central moment, as does the normal distribution. Here the probability density function decays exponentially, so all required moments are finite.
To compute , expand the denominator of the probability density function as a geometric series and integrate each nonnegative term. The gamma function integral gives the raw moments
Thus and . The following R code implements both checks for chosen interior parameters and large n:
p <- 0.4
beta <- 1.3
n <- 100000
raw_m <- vapply(1:4, function(j) {
  -factorial(j) * polylog(j + 1, 1 - p) / (beta^j * log(p))
}, numeric(1))
m <- raw_m[1]
v <- raw_m[2] - m^2
mu4 <- raw_m[4] - 4 * m * raw_m[3] +
  6 * m^2 * raw_m[2] - 3 * m^4
x <- r_sample_f(n, p, beta)
c(mean_check = sqrt(n) * (mean(x) - m) / sqrt(v),
  variance_check = sqrt(n) * (var(x) - v) / sqrt(mu4 - v^2))
For each check separately, an absolute value below is consistent with its approximate confidence interval. Repeat with many independent samples: the two standardized statistics should each resemble a standard normal distribution, with about outside that range. Also vary , and n; a systematic discrepancy that grows with suggests a bias. The two checks from the same sample are generally dependent, so passing both is not itself a joint statement, nor can a finite test prove that a generator is correct.
The second central limit theorem follows from the central limit theorem for the sample variance: differs by a quantity tending to zero in probability from .
Taking the logarithm of the normalized probability density function, then summing over the independent observations, gives the log-likelihood
Both and are parameters being inferred, so their contributions to the normalizing constant must be retained. The following R implementation validates the arguments and returns one number:
ll_f <- function(p, beta, x) {
  scalar <- function(v) {
    is.numeric(v) && !is.complex(v) && length(v) == 1L &&
      is.finite(v)
  }
  if (!scalar(p) || p <= 0 || p >= 1)
    stop("p must lie strictly between 0 and 1")
  if (!scalar(beta) || beta <= 0)
    stop("beta must be positive")
  if (!is.numeric(x) || is.complex(x) || !is.null(dim(x)) ||
      any(!is.finite(x)) || any(x <= 0))
    stop("x must be a vector of finite positive numbers")
  if (length(x) == 0L) return(0)
  t <- log1p(-p) - beta * x
  length(x) * (log(beta) - log(-log(p))) +
    sum(t - log(-expm1(t)))
}
Here and -expm1(t) computes accurately near zero. The empty-data likelihood function is one, so its log-likelihood is zero. Invalid parameter values produce errors in this evaluator; the next algorithm rejects candidates outside the parameter support before calling it.
The following R code implements the given recursion for the Cholesky decomposition. The input is assumed to be a real symmetric matrix that is a positive-definite matrix.
mychol <- function(A) {
  n <- nrow(A)
  if (n == 1L) return(matrix(sqrt(A[1L, 1L]), 1L, 1L))
  j <- 2L:n
  L <- matrix(0, n, n)
  L[1L, 1L] <- sqrt(A[1L, 1L])
  b <- A[j, 1L, drop = FALSE] / L[1L, 1L]
  L[j, 1L] <- b
  S <- A[j, j, drop = FALSE] - tcrossprod(b)
  L[j, j] <- mychol(S)
  L
}
Here tcrossprod(b) is the outer product . The drop = FALSE arguments preserve matrix dimensions, including the final one-by-one subproblem, and the base case also returns a matrix.
To verify correctness, partition . The code sets , and recursively factors . This Schur complement is a positive-definite matrix: for ,
If is the recursive answer, then the returned lower triangular matrix satisfies
The one-dimensional identity starts this mathematical induction proof. Each recursive call reduces the matrix size by one and therefore terminates.
The Cholesky decomposition gives . If , reversal of factors in a matrix inverse gives
Hence the required R implementation is
invsigma <- function(sigma) {
  L <- mychol(sigma)
  B <- ltinv(L)
  crossprod(B)
}
Here crossprod(B) computes . Using tcrossprod(B) would compute , which generally differs from . Both helper functions preserve the one-by-one matrix case.
Choose distinct coordinates uniformly for the starting ones; each element of then has starting probability . At each iteration choose one current one and one current zero uniformly and swap them. This implements the normalized proposal distribution from (c)(i), remains in , and needs just one precomputed matrix inverse.
mymcmc <- function(x, k, sigma) {
  n <- length(x)
  stopifnot(length(k) == 1L, is.finite(k), k == floor(k),
            k > 0L, k < n, is.matrix(sigma),
            all(dim(sigma) == c(n, n)))
  sigma_inv <- invsigma(sigma)
  u <- integer(n)
  u[sample.int(n, k, replace = FALSE)] <- 1L
  draws <- matrix(0, 100L, n)
  for (iteration in seq_len(100L)) {
    ones <- which(u == 1L)
    zeros <- which(u == 0L)
    i <- ones[sample.int(length(ones), 1L)]
    j <- zeros[sample.int(length(zeros), 1L)]
    v <- u
    v[i] <- 0L
    v[j] <- 1L
    alpha <- mylogapp(x, u, v, sigma_inv)
    if (runif(1L) < alpha) u <- v
    draws[iteration, ] <- u
  }
  colMeans(draws)
}
Sampling positions with sample.int and then indexing into ones or zeros also works when or . In R, a call such as sample(ones, 1) would incorrectly sample from 1:ones when ones is a single numeric value greater than one.
The code records the state after each of exactly transitions, including repetitions after rejection. Its answer is the vector Monte Carlo estimator
This estimates the posterior mean coordinate by coordinate; because each coordinate is binary, its entries estimate the posterior probabilities of inclusion. Every stored state has coordinate sum , so the estimated means lie in and sum to .
For neighbouring states, the symmetry of the proposal and the Metropolis–Hastings acceptance probability give detailed balance:
Thus the posterior distribution is a stationary distribution. Any two states in can be connected by moving the ones that occupy the wrong positions, and the multivariate normal distribution gives strictly positive target masses. The resulting Markov chain is therefore an irreducible Markov chain, and its long-run state averages estimate the posterior mean.
The requested iterations are a finite approximation, without a convergence guarantee or discarded burn-in. A randomly chosen starting subset has the prior distribution, rather than generally the posterior distribution. No aperiodicity assumption is needed for the finite-state Markov chain ergodic theorem for averages; indeed a uniform two-state target with gives a period-two alternating chain. This does not invalidate either the code or the state-average estimator.
R (programming language) 2026-10-24
R is a programming language and environment for statistics, data analysis and graphics. For matrix algorithms, drop = FALSE preserves dimensions when extracting submatrices. To choose an element uniformly from a possibly singleton numeric vector indices, use indices[sample.int(length(indices), 1L)]: sample(indices, 1L) has a special interpretation when its first argument is a single positive number. Official documentation: Extraction and the drop argument and Sampling, including the singleton numeric argument rule.