zoukankan      html  css  js  c++  java
  • Java:Random函数及其种子的作用

    伪随机(preundorandom):通过算法产生的随机数都是伪随机!!

    只有通过真实的随机事件产生的随机数才是真随机!!比如,通过机器的硬件噪声产生随机数、通过大气噪声产生随机数

    Random生成的随机数都是伪随机数!!!

    是由可确定的函数(常用线性同余),通过一个种子(常用时钟),产生的伪随机数。这意味着:如果知道了种子,或者已经产生的随机数,都可能获得接下来随机数序列的信息(可预测性) 

    Random类拥有两个构造方法,用于实现随机数生成器:

    Random( ) 构造一个随机数生成器
    Random(long seed) 用种子seed构造一个随机数生成器

    一、无参构造方法(不设置种子)

    虽然表面上看我们未设置种子,但Random构造方法里有一套自己的种子生成机制,源码如下:

     1 /**
     2      * Creates a new random number generator. This constructor sets
     3      * the seed of the random number generator to a value very likely
     4      * to be distinct from any other invocation of this constructor.
     5      */
     6     public Random() {
     7         this(seedUniquifier() ^ System.nanoTime());
     8     }
     9 
    10     private static long seedUniquifier() {
    11         // L'Ecuyer, "Tables of Linear Congruential Generators of
    12         // Different Sizes and Good Lattice Structure", 1999
    13         for (;;) {
    14             long current = seedUniquifier.get();
    15             long next = current * 181783497276652981L;
    16             if (seedUniquifier.compareAndSet(current, next))
    17                 return next;
    18         }
    19     }
    20 
    21     private static final AtomicLong seedUniquifier
    22         = new AtomicLong(8682522807148012L);

    生成种子过程:(参考解密随机数生成器(二)——从java源码看线性同余算法

    1、获得一个长整形数作为“初始种子”(系统默认的是8682522807148012L)

    2、不断与一个变态的数——181783497276652981L相乘(天知道这些数是不是工程师随便滚键盘滚出来的-.-)得到一个不能预测的值,直到 能把这个不能事先预期的值 赋给Random对象的静态常量seedUniquifier 。因为多线程环境下赋值操作可能失败,就for(;;)来保证一定要赋值成功

    3、与系统随机出来的nanotime值作异或运算,得到最终的种子

    nanotime算是一个随机性比较强的参数,用于描述代码的执行时间。源码中关于nanotime的描述(部分):

    /**
         * Returns the current value of the running Java Virtual Machine's
         * high-resolution time source, in nanoseconds.
         *
         * <p>This method can only be used to measure elapsed time and is
         * not related to any other notion of system or wall-clock time.

    二、有参构造方法(设置种子)

    语法:Random ran = Random(long seed)

    有参构造方法的源码如下:

     1 /**
     2      * Creates a new random number generator using a single {@code long} seed.
     3      * The seed is the initial value of the internal state of the pseudorandom
     4      * number generator which is maintained by method {@link #next}.
     5      *
     6      * <p>The invocation {@code new Random(seed)} is equivalent to:
     7      *  <pre> {@code
     8      * Random rnd = new Random();
     9      * rnd.setSeed(seed);}</pre>
    10      *
    11      * @param seed the initial seed
    12      * @see   #setSeed(long)
    13      */
    14     public Random(long seed) {
    15         if (getClass() == Random.class)
    16             this.seed = new AtomicLong(initialScramble(seed));
    17         else {
    18             // subclass might have overriden setSeed
    19             this.seed = new AtomicLong();
    20             setSeed(seed);
    21         }
    22     }
    23 
    24     private static long initialScramble(long seed) {
    25         return (seed ^ multiplier) & mask;
    26     }

    其中的multiplier和mask都是定值:

    1  private static final long multiplier = 0x5DEECE66DL;
    2 
    3  private static final long mask = (1L << 48) - 1;

    三、代码测试

    分别采用有参和无参两种方法,生成[0, 100)内的随机整数,各生成五组,每组十个随机数:

     1 import java.util.Random;
     2 
     3 public class RandomTest {
     4     public static void main(String[] args) {
     5         RandomTest rt = new RandomTest();
     6         rt.testRandom();
     7     }
     8 
     9     public void testRandom(){
    10         System.out.println("Random不设置种子:");
    11         for (int i = 0; i < 5; i++) {
    12             Random random = new Random();
    13             for (int j = 0; j < 10; j++) {
    14                 System.out.print(" " + random.nextInt(100) + ", ");
    15             }
    16             System.out.println("");
    17         }
    18 
    19         System.out.println("");
    20 
    21         System.out.println("Random设置种子:");
    22         for (int i = 0; i < 5; i++) {
    23             Random random = new Random();
    24             random.setSeed(100);
    25             for (int j = 0; j < 10; j++) {
    26                 System.out.print(" " + random.nextInt(100) + ", ");
    27             }
    28             System.out.println("");
    29         }
    30     }
    31 }

    运行结果如下:

    结论:

    虽然二者都是伪随机,但是,无参数构造方法(不设置种子)具有更强的随机性,能够满足一般统计上的随机数要求。使用有参的构造方法(设置种子)无论你生成多少次,每次生成的随机序列都相同,名副其实的伪随机!!

  • 相关阅读:
    LeetCode Path Sum II
    LeetCode Longest Palindromic Substring
    LeetCode Populating Next Right Pointers in Each Node II
    LeetCode Best Time to Buy and Sell Stock III
    LeetCode Binary Tree Maximum Path Sum
    LeetCode Find Peak Element
    LeetCode Maximum Product Subarray
    LeetCode Intersection of Two Linked Lists
    一天一个设计模式(1)——工厂模式
    PHP迭代器 Iterator
  • 原文地址:https://www.cnblogs.com/huiAlex/p/8057737.html
Copyright © 2011-2022 走看看