[ 
https://issues.apache.org/jira/browse/RNG-176?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17530036#comment-17530036
 ] 

Alex Herbert commented on RNG-176:
----------------------------------

{quote}duplicate non-trivial functionality
{quote}
Yes. However note that the methods to generate a float in a range are not in 
the sampling module. This only has samples for int, long and double in a range.

The interface methods for ranges would be non-optimised. For a single 
generation this lack of optimisation will be a benefit over having to create a 
sampler class to obtain a single number. For the streams then the lack of 
optimisation would be a negative.

In the case of low frequency generation of random numbers in a range then the 
following is my preferred method:
{code:java}
double lo = 1.33;
double hi = 4.66;

// No RNG available
double d = ThreadLocalRandom.current().nextDouble(lo, hi);

UniformRandomProvider rng;
double d = lo + rng.nextDouble() * (hi - lo); 
{code}
The nextDouble(lo, hi) method effectively does exactly as specified but then 
adds a check that the upper bound is exclusive, which can occur due to rounding:
{noformat}
jshell> double lo = 3.5
lo ==> 3.5

jshell> double hi = 4.5
hi ==> 4.5

jshell> lo + Math.nextDown(1.0) * (hi - lo)
$14 ==> 4.5
{noformat}
I am finding that I am increasingly using the JDKs streaming methods for quick 
generation of random numbers, typically into arrays of test random data. For 
heavy generation in simulations I would use samplers, typically created once 
then shared out to threads via an Executor and a Jumpable RNG.

I do not really see any harm in adding the methods. It provides a convenience 
for consumers of the interface and brings in parity with the RandomGenerator 
interface.

This is the type of code I think the convenience methods are used for:
{code:java}
double[] data = new SplittableRandom().doubles(50, lo, hi).toArray();

// Current
UniformRandomProvider rng = ...;
double[] data = DoubleStream.generate(
    ContinuousUniformSampler.of(rng, lo, hi)::sample)
    .limit(50).toArray();

// New
double[] data = rng.doubles(50, lo, hi).toArray();
{code}
So here we are saving not many characters of typing but it does not require 
inclusion of the sampling module, or even knowledge of how to do this with the 
DoubleStream.

 

> Enhance the UniformRandomProvider interface with extra methods and default 
> implementations
> ------------------------------------------------------------------------------------------
>
>                 Key: RNG-176
>                 URL: https://issues.apache.org/jira/browse/RNG-176
>             Project: Commons RNG
>          Issue Type: New Feature
>    Affects Versions: 1.4
>            Reporter: Alex Herbert
>            Assignee: Alex Herbert
>            Priority: Major
>
> JDK 17 introduced the {{RandomGenerator}} interface with the following 
> methods:
> {code:java}
> DoubleStream doubles();
> DoubleStream doubles(double randomNumberOrigin, double randomNumberBound);
> DoubleStream doubles(long streamSize);
> DoubleStream doubles(long streamSize, double randomNumberOrigin,
>                      double randomNumberBound);
> IntStream ints();
> IntStream ints(int randomNumberOrigin, int randomNumberBound);
> IntStream ints(long streamSize);
> IntStream ints(long streamSize, int randomNumberOrigin,
>                int randomNumberBound);
> LongStream longs();
> LongStream longs(long randomNumberOrigin, long randomNumberBound);
> LongStream longs(long streamSize);
> LongStream longs(long streamSize, long randomNumberOrigin,
>                  long randomNumberBound);
> boolean nextBoolean();
> void nextBytes(byte[] bytes);
> float nextFloat();
> float nextFloat(float bound);
> float nextFloat(float origin, float bound);
> double nextDouble();
> double nextDouble(double bound);
> double nextDouble(double origin, double bound);
> int nextInt();
> int nextInt(int bound);
> int nextInt(int origin, int bound);
> long nextLong();
> long nextLong(long bound);
> long nextLong(long origin, long bound);
> double nextGaussian();
> double nextGaussian(double mean, double stddev);
> double nextExponential();
> {code}
> The only method that is *non-default* is {{{}nextLong{}}}. This allows a new 
> generator to be simply implemented by providing the source of randomness as 
> 64-bit longs.
> The {{UniformRandomProvider}} interface can be expanded to include these 
> generation methods. Using Java 8 default interface methods will not require 
> any changes to generators currently implementing the interface.
> I propose to:
>  # Add the new methods for streams and numbers in a range.
>  # Add default implementations of the current API. These can be extracted 
> from the  o.a.c.rng.core.BaseProvider implementations.
>  # Remove the implementations in o.a.c.rng.core.BaseProvider. This change 
> would be binary compatible.
> The base classes in commons core for 32-bit and 64-bit sources of randomness, 
> IntProvider and LongProvider, can be updated suitably to only override the 
> default interface methods where they can be more efficiently implemented 
> given the source of randomness. This applies to:
> ||Source||Update||Details||
> |int|nextBytes|Use nextInt() for the source of bytes|
> | |nextBoolean|Use a cached int for the randomness|
> | |nextInt|Directly supply the int rather than using 32-bits from nextLong()|
> | |nextDouble|Optimise the bits used from two ints for the 53-bits required 
> for the double.|
> |long|nextInt; nextBoolean|Use a cached long for the randomness|
> h3. Note 1
> The UniformRandomProvider also has the method:
> {code:java}
> void nextBytes(byte[] bytes,
>                int start,
>                int len);
> {code}
> This can also have a default implementation using the output from nextLong().
> h3. Note 2
> The methods to generate an exponential and Gaussian are already implemented 
> in the {{commons-rng-sampling}} module.
> java.util.Random has a nextGaussian() method and so this method appears to be 
> for backward compatibility with legacy Java code. The method is implemented 
> using a modified Ziggurat sampler which uses an exponential sampler for the 
> long tail. The API has thus exposed the exponential sampling method that is 
> used internally in the nextGaussian implementation.
> With no backward compatibility requirements the Commons RNG interface can 
> avoid the distribution sampling methods. Users should select an appropriate 
> sampler from the sampling module.
>  



--
This message was sent by Atlassian Jira
(v8.20.7#820007)

Reply via email to