001package squidpony.squidmath; 002 003/** 004 * A way to take an {@link IRNG} and get one or more random numbers from it to produce a double in some statistical 005 * distribution, such as Gaussian (also called the normal distribution), exponential, or various simpler schemes that 006 * don't have common mathematical names. An example of the last category is "spike" for a distribution that is very 007 * likely to be 0 and quickly drops off to being less likely for positive or negative results between 0 and -1 or 1, or 008 * "bathtub" for the "spike" distribution's fractional part from 0 to 1 (which is likely to be 0 or 1 and very unlikely 009 * to be near 0.5). 010 * Created by Tommy Ettinger on 11/23/2019. 011 */ 012public interface IDistribution { 013 /** 014 * Gets a double between {@link #getLowerBound()} and {@link #getUpperBound()} that obeys this distribution. 015 * @param rng an IRNG, such as {@link RNG} or {@link GWTRNG}, that this will get one or more random numbers from 016 * @return a double within the range of {@link #getLowerBound()} and {@link #getUpperBound()} 017 */ 018 double nextDouble(IRNG rng); 019 020 /** 021 * Gets the lower bound of the distribution. The documentation should specify whether the bound is inclusive or 022 * exclusive; if unspecified, it can be assumed to be inclusive (like {@link IRNG#nextDouble()}). 023 * @return the lower bound of the distribution 024 */ 025 double getLowerBound(); 026 /** 027 * Gets the upper bound of the distribution. The documentation should specify whether the bound is inclusive or 028 * exclusive; if unspecified, it can be assumed to be exclusive (like {@link IRNG#nextDouble()}). 029 * @return the upper bound of the distribution 030 */ 031 double getUpperBound(); 032 033 abstract class SimpleDistribution implements IDistribution { 034 035 /** 036 * Makes a new SimpleDistribution implementation given any IDistribution (typically one with large or infinite 037 * bounds) by getting the fractional component of a result from {@code otherDistribution}. 038 * @param otherDistribution any other IDistribution 039 * @return a new anonymous implementation of SimpleDistribution that gets the fractional part of {@code otherDistribution}. 040 */ 041 public static SimpleDistribution fractionalDistribution(final IDistribution otherDistribution) 042 { 043 return new SimpleDistribution() { 044 @Override 045 public double nextDouble(IRNG rng) { 046 final double v = otherDistribution.nextDouble(rng); 047 return v - (v >= 0.0 ? (int) v : (int)v - 1); 048 } 049 }; 050 } 051 /** 052 * Makes a new SimpleDistribution implementation given any IDistribution (typically one with large or infinite 053 * bounds) by getting the fractional component of {@code offset} plus a result from {@code otherDistribution}. 054 * Using the offset allows distributions like {@link GaussianDistribution} to become centered halfway on 0.5, 055 * making the result of this distribution have a Gaussian-like peak on 0.5 instead of of peaking at the bounds 056 * when offset is 0.0. 057 * @param otherDistribution any other IDistribution 058 * @return a new anonymous implementation of SimpleDistribution that gets the fractional part of {@code otherDistribution}. 059 */ 060 public static SimpleDistribution fractionalOffsetDistribution(final IDistribution otherDistribution, final double offset) 061 { 062 return new SimpleDistribution() { 063 @Override 064 public double nextDouble(IRNG rng) { 065 final double v = otherDistribution.nextDouble(rng) + offset; 066 return v - (v >= 0.0 ? (int) v : (int)v - 1); 067 } 068 }; 069 } 070 071 /** 072 * Makes a new SimpleDistribution implementation given any IDistribution (typically one with large or infinite 073 * bounds) by simply clamping results that are below 0 to 0 and at least 1 to 0.9999999999999999 (the largest 074 * double less than 1.0 than can be represented). This will behave very oddly for distributions that are 075 * centered on 0.0. 076 * @param otherDistribution any other IDistribution 077 * @return a new anonymous implementation of SimpleDistribution that clamps {@code otherDistribution} in range. 078 */ 079 public static SimpleDistribution clampedDistribution(final IDistribution otherDistribution) 080 { 081 return new SimpleDistribution() { 082 @Override 083 public double nextDouble(IRNG rng) { 084 return Math.max(0.0, Math.min(0.9999999999999999, otherDistribution.nextDouble(rng))); 085 } 086 }; 087 } 088 089 /** 090 * Gets the lower inclusive bound, which is 0.0. 091 * 092 * @return the lower inclusive bound of the distribution, 0.0 093 */ 094 @Override 095 public double getLowerBound() { 096 return 0.0; 097 } 098 099 /** 100 * Gets the upper exclusive bound of the distribution, which is 1.0. 101 * 102 * @return the upper exclusive bound of the distribution, 1.0 103 */ 104 @Override 105 public double getUpperBound() { 106 return 1.0; 107 } 108 } 109}