Distribution of Random Numbers

前端 未结 5 860
孤独总比滥情好
孤独总比滥情好 2021-02-04 17:22

I have two options of code:

Option 1

int myFunc() {
  return new Random().nextInt();
}

Or:

Option 2

5条回答
  •  攒了一身酷
    2021-02-04 17:34

    Quick Code:

    // For occasional tasks that just need an average quality random number
    ExecutorService threadPool = Executors.newCachedThreadPool();
    threadPool.execute( () -> {
      ThreadLocalRandom.current().nextInt(); // Fast and unique!
    } );
    
    
    // For SecureRandom, high quality random number
    final Random r = new SecureRandom();
    ExecutorService threadPool = Executors.newCachedThreadPool();
    threadPool.execute( () -> {
      r.nextInt(); // sun.security.provider.NativePRNG uses singleton.  Can't dodge contention.
    } );
    
    
    // Apache Common Math - Mersenne Twister - decent and non-singleton
    int cpu = Runtime.getRuntime().availableProcessors();
    ExecutorService executor = Executors.newFixedThreadPool( cpu );
    Map random = new WeakHashMap<>( cpu, 1.0f );
    
    executor.execute( ()-> {
       RandomGenerator r;
       synchronized ( random ) { // Get or create generator.
          r = random.get( Thread.currentThread() );
          if ( r == null ) random.put( Thread.currentThread(), r = new MersenneTwister() );
       }
       r.nextInt( 1000 );
    } );
    

    Explanation:

    1. Two Random of same seed will yield same numbers.
      1. Thus we will focus on whether we can guarantee different seeds.
    2. In theory, new Random() in each thread does not guarantee different seed.

      1. new Random is seeded by nanoTime and a "unique" number.
      2. The number is not guaranteed unique because its calculation is not synchronized.
      3. As for nanoTime, it guarantees to be "at least at good as currentTimeMillis"
      4. currentTimeMillis does not guarantee anything and can be pretty coarse.
      5. In real life, the two times are the same only on old linux systems and Win 98.
    3. In practice, new Random() in each thread basically always get different seeds.

      1. Creating thread is expensive. Mine creates 1 per 50,000ns. And that's not slow.
      2. 50µs is way above nanoTime's common granularities of up to a few ten ns.
      3. The unique number calculation (1.2) is also fast, so getting same number is very rare.
      4. Use Executors to create a thread pool to avoid the heavy new thread overhead.
    4. zapl suggested ThreadLocalRandom.current().nextInt(). Great idea.

      1. It does not create new Random, but it is also a linear congruential generator.
      2. It generate a new random for each call thread as that thread's seed.
      3. It is built to be very fast in multi-thread. (See notes below.)
      4. It is statically seeded by SecureRandom, which produce better quality random numbers.
    5. "uniformally distributed" is just one small part of randomness tests.

      1. Random is somewhat uniform, and its result can be predicted given just two values.
      2. SecureRandom guarantees this won't happens. (i.e. cryptographically strong)
      3. There is no risk of seed collision if you create a new SecureRandom in each thread.
      4. But currently its source is single thread anyway, no parallel generation.
      5. For a good RNG that supports multi-thread, find external help like Apache Common's MT.

    Note: Implementation details deduced from Java 8 source code. Future Java version may change; for example, ThreadLocalRandom is using sun.misc.Unsafe to store the seeds, which may be removed in Java 9 forcing ThreadLocalRandom to find a new way to work without contention.

提交回复
热议问题