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 19 import org.apache.commons.rng.UniformRandomProvider; 20 21 /** 22 * Sampling from an <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">exponential distribution</a>. 23 * 24 * <p>Sampling uses {@link UniformRandomProvider#nextDouble()}.</p> 25 * 26 * @since 1.0 27 */ 28 public class AhrensDieterExponentialSampler 29 extends SamplerBase 30 implements SharedStateContinuousSampler { 31 /** 32 * Table containing the constants 33 * \( q_i = sum_{j=1}^i (\ln 2)^j / j! = \ln 2 + (\ln 2)^2 / 2 + ... + (\ln 2)^i / i! \) 34 * until the largest representable fraction below 1 is exceeded. 35 * 36 * Note that 37 * \( 1 = 2 - 1 = \exp(\ln 2) - 1 = sum_{n=1}^\infinity (\ln 2)^n / n! \) 38 * thus \( q_i \rightarrow 1 as i \rightarrow +\infinity \), 39 * so the higher \( i \), the closer we get to 1 (the series is not alternating). 40 * 41 * By trying, n = 16 in Java is enough to reach 1. 42 */ 43 private static final double[] EXPONENTIAL_SA_QI = new double[16]; 44 /** The mean of this distribution. */ 45 private final double mean; 46 /** Underlying source of randomness. */ 47 private final UniformRandomProvider rng; 48 49 /** 50 * Initialize tables. 51 */ 52 static { 53 /** 54 * Filling EXPONENTIAL_SA_QI table. 55 * Note that we don't want qi = 0 in the table. 56 */ 57 final double ln2 = Math.log(2); 58 double qi = 0; 59 60 for (int i = 0; i < EXPONENTIAL_SA_QI.length; i++) { 61 qi += Math.pow(ln2, i + 1.0) / InternalUtils.factorial(i + 1); 62 EXPONENTIAL_SA_QI[i] = qi; 63 } 64 } 65 66 /** 67 * @param rng Generator of uniformly distributed random numbers. 68 * @param mean Mean of this distribution. 69 * @throws IllegalArgumentException if {@code mean <= 0} 70 */ 71 public AhrensDieterExponentialSampler(UniformRandomProvider rng, 72 double mean) { 73 super(null); 74 if (mean <= 0) { 75 throw new IllegalArgumentException("mean is not strictly positive: " + mean); 76 } 77 this.rng = rng; 78 this.mean = mean; 79 } 80 81 /** 82 * @param rng Generator of uniformly distributed random numbers. 83 * @param source Source to copy. 84 */ 85 private AhrensDieterExponentialSampler(UniformRandomProvider rng, 86 AhrensDieterExponentialSampler source) { 87 super(null); 88 this.rng = rng; 89 this.mean = source.mean; 90 } 91 92 /** {@inheritDoc} */ 93 @Override 94 public double sample() { 95 // Step 1: 96 double a = 0; 97 double u = rng.nextDouble(); 98 99 // Step 2 and 3: 100 while (u < 0.5) { 101 a += EXPONENTIAL_SA_QI[0]; 102 u *= 2; 103 } 104 105 // Step 4 (now u >= 0.5): 106 u += u - 1; 107 108 // Step 5: 109 if (u <= EXPONENTIAL_SA_QI[0]) { 110 return mean * (a + u); 111 } 112 113 // Step 6: 114 int i = 0; // Should be 1, be we iterate before it in while using 0. 115 double u2 = rng.nextDouble(); 116 double umin = u2; 117 118 // Step 7 and 8: 119 do { 120 ++i; 121 u2 = rng.nextDouble(); 122 123 if (u2 < umin) { 124 umin = u2; 125 } 126 127 // Step 8: 128 } while (u > EXPONENTIAL_SA_QI[i]); // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1. 129 130 return mean * (a + umin * EXPONENTIAL_SA_QI[0]); 131 } 132 133 /** {@inheritDoc} */ 134 @Override 135 public String toString() { 136 return "Ahrens-Dieter Exponential deviate [" + rng.toString() + "]"; 137 } 138 139 /** 140 * {@inheritDoc} 141 * 142 * @since 1.3 143 */ 144 @Override 145 public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) { 146 return new AhrensDieterExponentialSampler(rng, this); 147 } 148 149 /** 150 * Create a new exponential distribution sampler. 151 * 152 * @param rng Generator of uniformly distributed random numbers. 153 * @param mean Mean of the distribution. 154 * @return the sampler 155 * @throws IllegalArgumentException if {@code mean <= 0} 156 * @since 1.3 157 */ 158 public static SharedStateContinuousSampler of(UniformRandomProvider rng, 159 double mean) { 160 return new AhrensDieterExponentialSampler(rng, mean); 161 } 162 }