java生成随机数

白学
2023-12-01

问题来源

想写个小Demo,需要大量测试数据,就想到了随机数,但是在Random类Math.random()上犯迷糊了,就整理一下,以供以后参考。

Random类解释:

用于生成一个伪随机数,所谓伪随机数,就是只要种子一样,获得的随机数流的顺序就是一样的,类具体解释如下(ps:注意一下我标黑加粗的部分应该就行了)

An instance of this class is used to generate a stream of pseudorandom numbers; its period is only 248. The class uses a 48-bit seed, which is modified using a linear congruential formula. (See Donald E. Knuth, The Art of Computer Programming, Volume 2, Third edition: Seminumerical Algorithms, Section 3.2.1.)

此类的一个实例用于生成伪随机数流;它的周期仅为 248。该类使用 48 位种子,使用线性同余公式对其进行修改。 (参见 Donald E. Knuth,计算机编程艺术,第 2 卷,第三版:半数值算法,第 3.2.1 节。)
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. In order to guarantee this property, particular algorithms are specified for the class Random. Java implementations must use all the algorithms shown here for the class Random, for the sake of absolute portability of Java code. However, subclasses of class Random are permitted to use other algorithms, so long as they adhere to the general contracts for all the methods.

如果使用相同的种子创建 Random 的两个实例,并且对每个实例进行相同的方法调用序列,它们将生成并返回相同的数字序列。为了保证这个特性,为 Random 类指定了特定的算法。为了 Java 代码的绝对可移植性,Java 实现必须使用此处显示的所有 Random 类算法。但是,Random 类的子类可以使用其他算法,只要它们遵守所有方法的通用约定


The algorithms implemented by class Random use a protected utility method that on each invocation can supply up to 32 pseudorandomly generated bits.
Many applications will find the method Math.random simpler to use.


Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using java.util.concurrent.ThreadLocalRandom in multithreaded designs.

java.util.Random 的实例是线程安全的。但是,跨线程并发使用同一个 java.util.Random 实例可能会遇到争用,从而导致性能不佳。考虑在多线程设计中使用 java.util.concurrent.ThreadLocalRandom。


Instances of java.util.Random are not cryptographically secure. Consider instead using java.security.SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications.

java.util.Random 的实例不是加密安全的。考虑改为使用 java.security.SecureRandom 来获取加密安全的伪随机数生成器,以供对安全敏感的应用程序使用。
Since:
1.0
Author:
Frank Yellin

Random类具体方法:

首先生成Random对象,代码如下

Random random = new Random();

Random没有参数的话,默认用系统时间作为种子,也推荐这样,当然也可以传进去一个long类型的参数

// 无参构造函数
public Random() {
        this(seedUniquifier() ^ System.nanoTime());
    }
// 有参构造函数   
public Random(long seed) {
        if (getClass() == Random.class)
            this.seed = new AtomicLong(initialScramble(seed));
        else {
            // subclass might have overridden setSeed
            this.seed = new AtomicLong();
            setSeed(seed);
        }
    }

有了Random对象random,我将Random的方法根据使用方法(不是根据底层实现)分成了三类

1,nextInt()类

        // 生成一个int取值范围内随机整数(Integer.MIN_VALUE < x < Integer.MAX_VALUE)
        int i = random.nextInt();
        // 生成一个大于等于1,小于10的随机整数
        int j = random.nextInt(1,10);
        // 生成一个0到给定参数之间的伪随机值,每个值出现的概率大约相等,比如下面的例子会生成一个大于等于0,小于10的随机数
        int k = random.nextInt(10);;

这类是最常用的,本类型其他的方法还有

       // double类型3个
        double d = random.nextDouble();
        double d1 = random.nextDouble(1, 10);
        double d2 = random.nextDouble(10);
         boolean类型一个
        random.nextBoolean();

       // float类型3个
        float f = random.nextFloat();
        float f1 = random.nextFloat(1, 10);
        float f2 = random.nextFloat(10);
        // long类型3个
        long l = random.nextLong();
        long l1 = random.nextLong(0, 10);
        long l2 = random.nextLong(10);

        // 生成伪随机的高斯数
        // 高斯数2个
        double g = random.nextGaussian();
        double g1 = random.nextGaussian(0, 10);

        // 返回从均值为 1 的指数分布中伪随机选择的非负双精度值
        double v = random.nextExponential();

2,byte类型

 byte [] buffer = new byte[3];
 random.nextBytes(buffer);
 System.out.println(Arrays.toString(buffer));

这个类型的随机数是存在byte数组中的,一个byte只有8bit,存放数据大小 -128 <= x <=127

3,数据流类型

       // 下面4个例子不要同时运行,注释掉一个再运行下一个,因为数据一起打印出来看不出是哪个变量打印的

        //1,用于得到一个有效无限的伪随机int值流
        IntStream ints1 = random.ints();
        ints1.forEach(System.out::println);

        //2, 数据流大小为10,也就是打印10个数字后就停止了
        IntStream ints2 = random.ints(10);
        ints2.forEach(System.out::println);

        //3,用于得到一个有效无限的伪随机int值流,值位于[0,10) 内
        IntStream ints3 = random.ints(0, 10);
        ints3.forEach(System.out::println);

        //4, 数据流大小为5,值位于[0,10) 内
        IntStream ints4 = random.ints(5, 0, 10);
        ints4.forEach(System.out::println);

如果不终止程序,随机数将一直打印下去。类似的方法还有各种类型的数据流

        // double 4个
        DoubleStream doubles = random.doubles();
        DoubleStream doubles1 = random.doubles(10);
        DoubleStream doubles2 = random.doubles(0, 10);
        DoubleStream doubles3 = random.doubles(5, 0, 10);
        // long 4个
        LongStream longs = random.longs();
        LongStream longs1 = random.longs(10);
        LongStream longs2 = random.longs(0, 10);
        LongStream longs3 = random.longs(5, 0, 10);

上述方法底层基本都是下面这个代码块的函数传进去不同参数移位得到的,或得到返回值后进行一下加工操作,这里我感觉简单了解下就行,没必要深究。

    protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

Random的线程安全与加密安全问题:

基本就是上面解释中的说法,这里就是加一个例子

        ThreadLocalRandom rand = ThreadLocalRandom.current();
        int i = rand.nextInt(0,10);
        double d = rand.nextDouble(0,10); 

Random类与Math.random()方法:

根据查看Math.random()底层可知,它也是调用了Random类中的nextDouble()方法

 public static double random() {
        return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
    }

  private static final class RandomNumberGeneratorHolder {
        static final Random randomNumberGenerator = new Random();
    }

如果是生成整型数据之类的还是要用Random类。

 类似资料: