MarsagliaNormalizedGaussianSampler.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.rng.sampling.distribution;

  18. import org.apache.commons.rng.UniformRandomProvider;

  19. /**
  20.  * <a href="https://en.wikipedia.org/wiki/Marsaglia_polar_method">
  21.  * Marsaglia polar method</a> for sampling from a Gaussian distribution
  22.  * with mean 0 and standard deviation 1.
  23.  * This is a variation of the algorithm implemented in
  24.  * {@link BoxMullerNormalizedGaussianSampler}.
  25.  *
  26.  * <p>Sampling uses {@link UniformRandomProvider#nextDouble()}.</p>
  27.  *
  28.  * @since 1.1
  29.  */
  30. public class MarsagliaNormalizedGaussianSampler
  31.     implements NormalizedGaussianSampler, SharedStateContinuousSampler {
  32.     /** Next gaussian. */
  33.     private double nextGaussian = Double.NaN;
  34.     /** Underlying source of randomness. */
  35.     private final UniformRandomProvider rng;

  36.     /**
  37.      * Create an instance.
  38.      *
  39.      * @param rng Generator of uniformly distributed random numbers.
  40.      */
  41.     public MarsagliaNormalizedGaussianSampler(UniformRandomProvider rng) {
  42.         this.rng = rng;
  43.     }

  44.     /** {@inheritDoc} */
  45.     @Override
  46.     public double sample() {
  47.         if (Double.isNaN(nextGaussian)) {
  48.             // Rejection scheme for selecting a pair that lies within the unit circle.
  49.             while (true) {
  50.                 // Generate a pair of numbers within [-1 , 1).
  51.                 final double x = 2 * rng.nextDouble() - 1;
  52.                 final double y = 2 * rng.nextDouble() - 1;
  53.                 final double r2 = x * x + y * y;

  54.                 if (r2 < 1 && r2 > 0) {
  55.                     // Pair (x, y) is within unit circle.
  56.                     final double alpha = Math.sqrt(-2 * Math.log(r2) / r2);

  57.                     // Keep second element of the pair for next invocation.
  58.                     nextGaussian = alpha * y;

  59.                     // Return the first element of the generated pair.
  60.                     return alpha * x;
  61.                 }

  62.                 // Pair is not within the unit circle: Generate another one.
  63.             }
  64.         }

  65.         // Use the second element of the pair (generated at the
  66.         // previous invocation).
  67.         final double r = nextGaussian;

  68.         // Both elements of the pair have been used.
  69.         nextGaussian = Double.NaN;

  70.         return r;
  71.     }

  72.     /** {@inheritDoc} */
  73.     @Override
  74.     public String toString() {
  75.         return "Box-Muller (with rejection) normalized Gaussian deviate [" + rng.toString() + "]";
  76.     }

  77.     /**
  78.      * {@inheritDoc}
  79.      *
  80.      * @since 1.3
  81.      */
  82.     @Override
  83.     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
  84.         return new MarsagliaNormalizedGaussianSampler(rng);
  85.     }

  86.     /**
  87.      * Create a new normalised Gaussian sampler.
  88.      *
  89.      * @param <S> Sampler type.
  90.      * @param rng Generator of uniformly distributed random numbers.
  91.      * @return the sampler
  92.      * @since 1.3
  93.      */
  94.     @SuppressWarnings("unchecked")
  95.     public static <S extends NormalizedGaussianSampler & SharedStateContinuousSampler> S
  96.             of(UniformRandomProvider rng) {
  97.         return (S) new MarsagliaNormalizedGaussianSampler(rng);
  98.     }
  99. }