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

Java random随机数/ seed 种子 / System.nanoTime() 的理解 与 使用伪随机(preundorandom):通过算法产生的随机数都是伪随机!! 只有通过真实的随机事件产生的随机数才是真随机!!比如,通过机器的硬件噪声产生随机数、通过大气噪声产生随机数 Random生成的

伪随机(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都是定值:

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 }

运行结果如下:

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

结论:

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

四、为何要使用种子?

种子就是生成随机数的根,就是产生随机数的基础。计算机的随机数都是伪随机数,以一个真随机数(种子)作为初始条件,然后用一定的算法不停迭代产生随机数。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://bianchenghao.cn/21105.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注