0%

Java和Kotlin的N种Random方式

日常开发中少不了产生随机数,本文就分别给大家总结一下Javah和Kotlin中常用的Random方式

简介

随机数是专门的随机试验的结果.
大部分计算机上的伪随机数,并不是真正的随机数,只是重复的周期比较大的数列,是按一定的算法和种子值生成的。
生成真随机数
著名的 Random.org 网站可以用来生成真随机数。它是通过大气噪音 (Atmospheric Noise)生成随机数,由爱尔兰都柏林三一学院Mads Haahr博士于1998创建。
random和urandom设备
/dev/random和/dev/urandom是Linux系统中提供的随机伪设备,这两个设备的任务,是提供永不为空的随机字节数据流。

1. Java 方式

1.1 Math.random()

Math类(java.lang.Math)中的random方法可以产生一个 [0.0,1.0) 之间的类型为double的随机数.

1
2
3
4
5
6
7
8
public static void main(String[] args) {
System.out.println( Math.random());
System.out.println( Math.random());
}

Result:
0.23517533008621505
0.05269740773586806

1.2 Random类

1.2.1 关于初始化和种子

随机种子(Random Seed)是计算机专业术语,一种以随机数作为对象的以真随机数(种子)为初始条件的随机数。

Random类包含两个构造函数:

  • 默认构造函数采用以当前时间为种子的方式。
  • 指定种子的构造方式
    1
    2
    Random random1 = new Random();
    Random random2 = new Random(100);
    相同种子数的Random对象,相同次数生成的随机数字是完全相同的。如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        public static void main(String[] args) {
    Random random = new Random(100);
    Random random2 = new Random(100);
    System.out.println( random.nextInt());
    System.out.println( random2.nextInt());
    }

    Result:
    -1193959466
    -1193959466

1.2.2 成员方法

返回类型 函数名 含义
boolean nextBoolean() 返回下一个伪随机数它是取自此随机数生成器序列的均匀分布的boolean值。
void nextBytes(byte[] bytes) 生成随机字节并将其置于用户提供的 byte 数组中。
double nextDouble() 产生一个在0.0和1.0之间均匀分布的 double随机值。
float nextFloat() 产生一个在0.0和1.0之间均匀分布的 float随机值。
double nextGaussian() 返回double随机数,它是呈高斯(“正态”)分布的,其平均值是0.0标准差是1.0。
int nextInt() 产生一个均匀分布的 int 随机值。
int nextInt(int n) 产生一个在(包括和指定值(不包括)之间均匀分布的int值。
long nextLong() 产生一个均匀分布的 int 随机值。long 值。
void setSeed(long seed) 使用单个 long 种子设置此随机数生成器的种子

1.2.3 Math.random()实现

Math.random()方法是调用了 Random的nextDouble 产生的随机double值, 此方法需要注意的是如果连续调用两次,则可能产生相同的随机数,其原因可能因为两条指令执行时间太近导致Random的种子一致。

1.2.4 随机数流

Java8中的Random类,增加一个以流的方式读取随机数的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Random random = new Random();

//返回一个Int类型的数据流,并读取10个
IntStream intStream = random.ints();
intStream.limit(10).forEach(s->System.out.print(s +" "));
System.out.print(" \n") ;

//返回一个Long类型的数据流,并读取10个
LongStream longStream = random.longs();
longStream.limit(10).forEach(s->System.out.print(s +" "));
System.out.print(" \n") ;

//返回一个Double类型的数据流,并读取10个
DoubleStream doubleStream = random.doubles();
doubleStream.limit(10).forEach(s->System.out.print(s + " " ));

其中IntStream LongStream DoubleStream 三个Stream均为Java8特性,提供了range(a,b)、
sum、sorted、limit等方法.

1.3 其他Random类

1.3.1 ThreadLocalRandom

通过官方介绍了解到,Random类是线程安全的,原因是多线程生成random使用的是同一个Random对象,正因为此,多线程生成random会影响系统性能。于是官方推荐了ThreadLocalRandom.
ThreadLocalRandom是结合了TreadLocal和Random,将每个线程中的random对象通过ThreadLocal保存,从而达到互相隔离的目的.
代码示例:

1
2
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current(); //使用current静态方法创建示例
int random = threadLocalRandom.nextInt();

注:与Random在使用上不同的是ThreadLocalRandom不能手动设置seed.

1.3.2 SecureRandom

由于上述中种子相同情况下产生的随机数相等的原因。于是处于更高级的随机性,便有了SecureRandom。
SecureRandom产生随机数的原理和Random并无二致,区别在于种子的选取上。 SecureRandom默认读取由/dev/random产生的随机值作为种子,大大增加了破解的难度。实例代码:

1
2
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextInt();

注:SecureRandom同时也提供了setSeed方法,但官方不推荐使用.

1.3.3 SplittableRandom

这是一个支持并行计算的随机数类,线程不安全。

1
2
SplittableRandom splittableRandom = new SplittableRandom();
int randomWithSplittableRandom = splittableRandom.nextInt(min, max);

2. Kotlin 方式

由于Kotlin本身可以调用Java类,所以上述的方法都可以在Kotlin中使用,以下介绍Kotlin中独有的方法创建随机数。

2.1 Range类的random

kotlin支持在Range类中直接取出random.其中包括:

  • IntRange
  • LongRange
  • CharRange
    代码实例:
    1
    var  num =(0..10).random()

2.2 Random类(package: kotlin.random)

Random中提供各种random函数的伴生对象实现:

  • Random.nextBits(bitcount): 随机生成不大于二进制位数不大于bitcount的随机数
  • Random.nextInt(): 随机生成一个在Int.MIN_VALUE 和 Int.MAX_VALUE 之间的随机数
  • Random.nextLong: 随机生成一个Long类型的数值