您的位置 首页 java

Java random随机数/ seed 种子 / System.nanoTime() 的理解 与 使用

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

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

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

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

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

  1. Random( ) 构造一个随机数生成器,种子是 与nanoTime异或后的值。每遍输出的多个序列均不同。随机性更强。
  2. Random(long seed) 用种子seed构造一个随机数生成器,种子是给定的。每遍输出的多个序列均相同。

源码:

     /**
     * Creates a new random number generator. This constructor sets
     * the seed of the random number generator to a value very likely
     * to be distinct from any other invocation of this constructor.
     */    public Random() {
        this(seedUniquifier() ^ System.nanoTime());//与System.nanoTime()异或
        //这里System.nanoTime();并不是以 纳秒 为单位的系统时间,只用来计算花费多少时间用的。与时间的概念无关。
    }
 
    private static long seedUniquifier() {
        // L'Ecuyer, "Tables of Linear Congruential Generators of
        // Different Sizes and Good Lattice Structure", 1999
        for (;;) {
            long current = seedUniquifier.get();
            long next = current * 181783497276652981L;
            if (seedUniquifier.compareAndSet(current, next))
                return next;
        }
    }
 
    private static final AtomicLong seedUniquifier
        = new AtomicLong(8682522807148012L); //种子分配器
 
    /**
     * Creates a new random number generator using a single {@code long} seed.
     * The seed is the initial value of the internal state of the pseudorandom
     *  number  generator which is maintained by method {@link #next}.
     *
     * <p>The invocation {@code new Random(seed)} is equivalent to:
     *  <pre> {@code
     * Random rnd = new Random();
     *  rnd .setSeed(seed);}</pre>
     *
     * @param seed the initial seed
     * @see   #setSeed(long)
     */    public Random(long seed) {//无参构造,有参构造,均是调用这个方法
        if (getClass() == Random.class)// Random random1 = new Random();或者  Random random2 = new Random(100); 均是调用这个if
            this.seed = new AtomicLong(initialScramble(seed));//处理后,seed值没变
        else {
            // subclass might have overriden setSeed
            this.seed = new AtomicLong();
            setSeed(seed);
        }
    }
 
    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
    }  

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

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

生成种子过程:(参考解密随机数生成器(二)——从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.
     * The value returned represents nanoseconds since some fixed but
     * arbitrary <i>origin</i> time (perhaps in the future, so values
     * may be negative).  The same origin is used by all invocations of
     * this method in an instance of a Java  virtual machine ; other
     * virtual machine instances are likely to use a different origin.
     *
     * <p>This method provides nanosecond precision, but not necessarily
     * nanosecond resolution (that is, how frequently the value changes)
     * - no guarantees are made except that the resolution is at least as
     * good as that of {@link #currentTimeMillis()}.
     *
     * <p>Differences in successive calls that span greater than
     * approximately 292 years (2<sup>63</sup> nanoseconds) will not
     * correctly compute elapsed time due to numerical overflow.
     *
     * <p>The values returned by this method become meaningful only when
     * the difference between two such values, obtained within the same
     * instance of a Java virtual machine, is computed.
     *
     * <p> For example, to measure how long some code takes to execute:
     *  <pre> {@code
     * long startTime = System.nanoTime();
     * // ... the code being measured ...
     * long estimatedTime = System.nanoTime() - startTime;}</pre>
     *
     * <p>To compare two nanoTime values
     *  <pre> {@code
     * long t0 = System.nanoTime();
     * ...
     * long t1 = System.nanoTime();}</pre>
     *
     * one should use {@code t1 - t0 < 0}, not {@code t1 < t0},
     * because of the possibility of numerical overflow.
     *
     * @return the current value of the running Java Virtual Machine's
     *         high-resolution time source, in nanoseconds
     * @since 1.5
     */    public static native long nanoTime();  

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

语法:Random ran = Random(long seed)

有参构造方法的源码如上。

其中的multiplier和mask都是定值:

 private static final long multiplier = 0x5DEECE66DL;

private static final long mask = (1L << 48) - 1;  

三、代码测试

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

  import java.util.Random;
 
 public class RandomTest {
     public static void main(String[] args) {
         RandomTest rt = new RandomTest();
         rt.testRandom();
     }
 
     public void testRandom(){
         System.out.println("Random不设置种子:");
         for (int i = 0; i < 5; i++) {
             Random random = new Random();
             for (int j = 0; j < 10; j++) {
                 System.out.print(" " + random.nextInt(100) + ", ");
             }
             System.out.println("");
         }
 
         System.out.println("");
 
         System.out.println("Random设置种子:");
         for (int i = 0; i < 5; i++) {
             Random random = new Random();
             random.setSeed(100);
             for (int j = 0; j < 10; j++) {
                 System.out.print(" " + random.nextInt(100) + ", ");
             }
             System.out.println("");
         }
     }
 }  

运行结果如下:

结论:

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

四、为何要使用种子?

种子就是生成随机数的根,就是产生随机数的基础。计算机的随机数都是伪随机数,以一个真随机数(种子)作为初始条件,然后用一定的算法不停迭代产生随机数。Java项目中通常是通过Math.random方法和Random类来获得随机数。

Random类中不含参构造方法每次都使用当前时间作为种子,而含参构造方法是以一个固定值作为种子。

随机数是种子经过计算生成的。

不含参的构造函数每次都使用当前时间作为种子,随机性更强

随机数的生成是从种子值开始。 如果反复使用同一个种子,就会生成相同的数字系列,产生不同序列的一种方法是使种子值与时间相关。

五、System.nanoTime()

         System.out.println(System.nanoTime());//并不是以纳秒为单位的系统时间
        System.out.println(System.nanoTime());
        System.out.println(System.nanoTime());
        System.out.println(System.currentTimeMillis());
//        第一遍运行:
//        281092180853691
//        281092180911666
//        281092180936920
//        1610767387212  =>  2021-01-16 11:23:07
 
 
//        第二遍运行:
//        281207286912695
//        281207286952070
//        281207286970745
//        1610767502318  

以上运行、源码基于jdk1.8

end

文章来源:智云一二三科技

文章标题:Java random随机数/ seed 种子 / System.nanoTime() 的理解 与 使用

文章地址:https://www.zhihuclub.com/187511.shtml

关于作者: 智云科技

热门文章

评论已关闭

7条评论

  1. One class of compounds with potential to impact both mitochondrial and glutamatergic function is the pyrimidines, including triacetyluridine TAU, cytidine and uridine

  2. Todos los NSAID son igualmente efectivos para reducir el dolor Our thoughts and prayers are with your family at the lose of a very dear lady

  3. 1L Solution Topical 70 mL 100mL Soap Topical 63 mL 100mL Spray Topical 63 mL 100mL Solution Percutaneous 42 mg 25mL Aerosol, spray Topical 70 g 100g Solution Topical 75 g 100g Liquid Topical 670 mg 1mL Liquid Topical 50 Prince philip, wang y

  4. Multiple actions of EGCG, the main component of green tea Compounds 1, 3, 5, 7, 9, 22, 27, 40, 42, 43, 51, 52, 53, and 69 exhibited an LC50, the concentration of test compound at which the percent surviving cells was 50, less 1 ОјM

  5. Monitor Closely 1 esomeprazole, dextroamphetamine 66, 67 Indeed, there are now numerous new SERMs eg, lasofoxifene, basedoxifene, arzoxifene, etc being evaluated

  6. loteprednol dexamethasone vs prednisolone asthma In addition, using urine to get these cells isn t as disgusting as you might think

网站地图