Monday, January 17, 2022

Java Tutorial: Generating Random Numbers

Chapters

Java Tutorial: Generating Random Numbers

In this tutorial, we're going to discuss different ways of generating random numbers in java.
Math.random() Method

Method Form: public static double random()
This method is located in java.lang.Math package. This method returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0. Returned values are chosen pseudorandomly with (approximately) uniform distribution from that range.

This example demonstrates Math.random().
public class SampleClass{

  public static void main(String[] args){
  
    double d = Math.random();
    String s = String.format("%.1f",d);
    System.out.println(s);
    
    //with multiplier
    d = Math.random() * 2;
    s = String.format("%.1f",d);
    System.out.println(s);
  }
}

Result(may vary)
0.6
1.5
java.util.Random Class

Instance of Random is used to generate a stream of pseudorandom numbers. If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers. More information can be read in the documentation.

This example demonstrates the Random class.
import java.util.Random;

public class SampleClass{
  
  public static void main(String[] args){
    Random random = new Random();
  
    System.out.println("Ints");
    for(int i = 0; i < 3; i++)
      System.out.println(random.nextInt() + " ");
    System.out.println();
  
    System.out.println("Doubles");
    for(int i = 0; i < 3; i++)
      System.out.printf("%.2f%n",random.nextDouble());
    System.out.println();
    
    System.out.println("Guassian");
    for(int i = 0; i < 3; i++)
      System.out.printf("%.2f%n",random.nextGaussian());
  }
}

Result(may vary)
Ints
-612467063
1655395407
2107210140

Doubles
0.46
0.40
0.25

Gaussian
0.90
-2.14
0.77
In the example above, I used this constructor of Random: new Random(). This constructor will automatically generate seeds for us everytime we invoke methods that starts random computation like nextInt(). nextInt() returns a randomed integer number. All 2^32 possible int values are produced with (approximately) equal probability.

nextDouble() returns a uniformly distributed double value between 0.0 and 1.0 from this random number generator's sequence. nextGaussian() Returns a Gaussian(normal distribution) distributed double value with mean 0.0 and standard deviation 1.0 from this random number generator's sequence.

Next, this example demonstrates random with explicit seed.
import java.util.Random;

public class SampleClass{
  
  public static void main(String[] args){
    Random random = new Random(1);
    
    System.out.println
    ("Initial random: " + random.nextInt());
    for(int i = 1; i <= 5; i++){
      random.setSeed(i);
      System.out.println("Random: " + random.nextInt());
    }
      
  }
}

Result(may vary)
Initial random: -1155869325
Random: -1155869325
Random: -1154715079
Random: -1155099828
Random: -1157023572
Random: -1157408321
Initial random and the first random are equal because they have the same seed which is 1. Remember that the algorithm used in the Random class is a pseudorandom governed by a mathematical formula.

Note: You should be knowledgeable about Stream class to understand the methods that I'm gonna demonstrate next.

nextInt() method have second form where we can put a limit or bounds to the returned random value. Take a look at this example.
import java.util.Random;

public class SampleClass{
  
  public static void main(String[] args){
    Random random = new Random();
  
    System.out.println("set1");
    //random between 0-9
    for(int i = 0; i < 3; i++)
      System.out.println(random.nextInt(10) + " ");
      
    System.out.println("set2");
    //random between 1-10
    for(int i = 0; i < 3; i++)
      System.out.println((random.nextInt(10)+1) + " ");
      
   System.out.println("set3");
    //random between 0 to -10
    for(int i = 0; i < 3; i++)
      System.out.println((random.nextInt(11)+(-10)) + " ");
    
  }
}

Result(may vary)
Set1
3
8
1
Set2
8
1
10
Set3
-1
-10
-5
Remember that the number in the parameter is exclusive. It means that the given value is not included in the range. For example, random.nextInt(10) has a range from 0-9 not 0-10. Random class has methods that return a specific variants of Stream class. doubles(), ints() and longs() return DoubleStream, IntStream and LongStream respectively.

This example demonstrates ints() method.
import java.util.Random;
import java.util.Optional;

public class SampleClass{
  
  public static void main(String[] args){
    Random random = new Random();
  
    int num = 
    random
    .ints()
    .findFirst()
    .getAsInt();
    
    System.out.println(num);
  }
}

Result(may vary)
-1994793268
ints() method returns an effectively unlimited stream of pseudorandom int values. Use short-circuiting operations to stop infinite stream like the example above. findFirst() is a short-circuiting terminal operation. The example above is applicable to doubles() and longs() with few modifications.

Next, this example demonstrates ints(int randomNumberOrigin, int randomNumberBound).
import java.util.Random;
import java.util.Optional;

public class SampleClass{
  
  public static void main(String[] args){
    Random random = new Random();
  
    int num = 
    random
    .ints(0,11)
    .findAny()
    .getAsInt();
    
    System.out.println(num);
  }
}

Result(may vary)
5
Just like the example above, the returned stream here is infinite. randomNumberBound is exclusive. This means that the exact range is between randomNumberOrigin-(randomNumberBound-1). This example is applicable to doubles(double randomNumberOrigin, double randomNumberBound) and longs(long randomNumberOrigin, long randomNumberBound) with few modifications.

Next, this example demonstrates ints(long streamSize).
import java.util.Random;

public class SampleClass{
  
  public static void main(String[] args){
    Random random = new Random();
  
    random
    .ints(10)
    .filter((t) -> 
    {
      if(t % 2 == 0)
        return true;
      else return false;
    })
    .forEach(System.out::println);
    
  }
}

Result(may vary)
1162373228
-483124408
-2060536350
351896346
1394074158
Unlike in the two previous examples before this example above, the returned stream here has finite elements. Thus, we are not required to use short-circuiting operations. streamSize is the size of the returned stream. This example is applicable to doubles(long streamSize) and longs(long streamSize).

Next, this example demonstrates ints(long streamSize, int randomNumberOrigin, int randomNumberBound).
import java.util.Random;

public class SampleClass{
  
  public static void main(String[] args){
    Random random = new Random();
  
    random
    .ints(5, 20, 41)
    .forEach(System.out::println);
    
  }
}

Result(may vary)
27
21
28
33
28
Just like the example that preceded this example above, the returned stream here is finite. randomNumberBound is exclusive. This means that the exact range is between randomNumberOrigin-(randomNumberBound-1). ints(long streamSize, int randomNumberOrigin, int randomNumberBound) is a combination of ints(int randomNumberOrigin, int randomNumberBound) and ints(long streamSize).

This example is applicable to doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) and longs(long streamSize, long randomNumberOrigin, long randomNumberBound).

java.util.concurrent.ThreadLocalRandom Class

Note: Most method of this class is identical to the method of Random class. Better read the Random class topic first before reading this topic.

ThreadLocalRandom is a random number generator (with period 2^64) isolated to the current thread. Like the global Random generator used by the Math class, a ThreadLocalRandom is initialized with an internally generated seed that may not otherwise be modified. When applicable, use of ThreadLocalRandom rather than shared Random objects in concurrent programs will typically encounter much less overhead and contention.

Use of ThreadLocalRandom is particularly appropriate when multiple tasks (for example, each a ForkJoinTask) use random numbers in parallel in thread pools. In Random class, we can use the method next(int bits) to get an integer random number that also atomatically updates its seed. Although, contention likely occurs if multiple threads access next(int bits).

As the description above says, using ThreadLocalRandom rather than shared Random objects in concurrent programs will typically encounter much less overhead and contention.

Usages of this class should typically be of the form: ThreadLocalRandom.current().nextX(...) (where X is Int, Long, etc). When all usages are of this form, it is never possible to accidentally share a ThreadLocalRandom across multiple threads.

This example demonstrates ThreadLocalRandom.
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SampleClass{
  
  public static void main(String[] args){
    System.out.println
    (Thread.currentThread().getName() +
     " | " + ThreadLocalRandom.current().nextInt(10));
    
    ExecutorService es = Executors.newFixedThreadPool(4);
    
    for(int i = 0; i < 4; i++)
      es.execute(() -> 
      {
        System.out.println
        (Thread.currentThread().getName() +
        " | " + ThreadLocalRandom.current().nextInt(10));
      });
    
    es.shutdown();
  }
}

Result(may vary)
main | 7
pool-1-thread-1: 0
pool-1-thread-3: 9
pool-1-thread-2: 1
pool-1-thread-4: 7
current() method returns the current thread's ThreadLocalRandom object. Methods of this object should be called only by the current thread, not by other threads. As the description implies, every thread has a ThreadLocalRandom object.

java.util.SplittableRandom Class

SplittableRandom is a generator of uniform pseudorandom values (with period 2^64) applicable for use in (among other contexts) isolated parallel computations that may generate subtasks. This class can split random-generating task into multiple subtasks by using the split() method.

There are important pieces of information in the documentation that I didn't put here. You should read it. This example demonstrates SplittableRandom.
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.SplittableRandom;

public class SampleClass{

  public static void main(String[] args){
    
    System.out.println("\nCreating "+
                       "ForkJoinPool...");
    ForkJoinPool fjp = ForkJoinPool.commonPool();
                       
    int result = fjp.invoke(new Randomize(new SplittableRandom()));
    System.out.println("Sum: " + result);
  }
}

class Randomize extends RecursiveTask<Integer>{

  private final SplittableRandom splitTask;
  private static final AtomicInteger ai =
  new AtomicInteger(0);
  
  Randomize(SplittableRandom splitTask){
    this.splitTask = splitTask;
  }
  
  protected Integer compute(){
    Integer result = 0;
    
    if(ai.incrementAndGet() < 3){
      Randomize s1 = new Randomize(splitTask.split());
      s1.fork();
      int rand = splitTask.ints(10, 20).findAny().getAsInt();
      Randomize s2 = new Randomize(splitTask);
      result = rand + s2.compute() + s1.join();
      System.out.println(Thread.
      currentThread().getName() + " | Partial Result | " +
      rand);
    }
    else{
      result = splitTask.ints(10, 20).findAny().getAsInt();
      System.out.println(Thread.
      currentThread().getName() + " | Partial Result | " +
      result);
    }
     
    return result;
  }
}

Result(may vary)
Creating ForkJoinPool...
FokJoinPool.commonPool-worker-1 | Partial Result | 17
FokJoinPool.commonPool-worker-2 | Partial Result | 15
main | Partial Result | 17
FokJoinPool.commonPool-worker-1 | Partial Result | 11
main | Partial Result | 14
Sum: 74
If you want to split a specific SplittableRandom, use this form of split:
split(RandomGenerator.SplittableGenerator source)
splits() is a new method added to java 17 that returns new pseudorandom number generators, each of which implements the RandomGenerator.SplittableGenerator interface. This method has four forms. Two of them returns an infinite stream of pseudorandom number generators. The remaining two returns a finite stream. You can read them in the documentation.

java.security.SecureRandom Class

Previous random generators are cryptographically weak. SecureRandom uses different types of algorithms that are cryptographically strong. If you need a secure random generator in an application like authenticator app, consider using SecureRandom.

This class provides a cryptographically strong random number generator (RNG). A cryptographically strong random number minimally complies with the statistical random number generator tests specified in FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. Additionally, SecureRandom must produce non-deterministic output.

Therefore any seed material passed to a SecureRandom object must be unpredictable, and all SecureRandom output sequences must be cryptographically strong, as described in RFC 4086: Randomness Requirements for Security. SecureRandom objects are safe for use by multiple concurrent threads. More information can be read in the documentation.

This example demonstrates SecureRandom.
import java.security.SecureRandom;

public class SampleClass{
  
  public static void main(String[] args){
    SecureRandom random = new SecureRandom();
    
    System.out.println("Algorithm: " + 
    random.getAlgorithm());
    
    random
    .ints(5, 20, 41)
    .forEach(System.out::println);
    
  }
}

Result(may vary)
Algorithm: DRBG
27
26
22
31
22
In java 17, when SecureRandom() constructor is used, the default alrogithm is DRBG or Deterministic Random Big Generator. SecureRandom can use different types of algorithm which can be seen in this article.

One of the best usage of SecureRandom is to generate random hashes. This example demonstrates generating salt.. One of the usage of salt is to secure passwords.
import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;

public class SampleClass{
  
  public static void main(String[] args)
                throws NoSuchAlgorithmException{
    SecureRandom random = 
    SecureRandom.getInstance("SHA1PRNG");
    
    System.out.println("Algorithm: " + 
    random.getAlgorithm());
    
    byte[] salt = new byte[16];
    random.nextBytes(salt);
    
    System.out.println("Salt");
    System.out.println(
    new BigInteger(1, salt).toString(16));
    
  }
}

Result(may vary)
Algorithm: SHA1PRNG
Salt
d66d227b210c...
Use getInstance(String algorithm) to use other algorithms. This method returns a SecureRandom object that implements the specified Random Number Generator (RNG) algorithm. getInstance() has many forms that can be seen in the documentation.

nextBytes(byte[] bytes)
generates random bytes based on the length of the byte array argument and put those generated random bytes in the array.

No comments:

Post a Comment