认识随机
// Test1
for(int i = 0; i < 5; i++) {
System.out.println(Math.random());
}
结果:
0.45904368955839225
0.11666896773864122
0.7465548543062558
0.07555413692957569
0.13414951911779593
Math.random方法如下,类似静态内部类实现单例的形式,在Math中定义了单例的Random实例,Math.random方法其实实在调用Random实例的nextDouble方法。
//Math.class
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
static final Random randomNumberGenerator = new Random();
}
继续看Random类中的nextDouble方法,可以看到next方法中很关键的一个参数是seed,每执行一次next,seed的值也会发生变化。
//Random.class
public double nextDouble() {
return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT; //DOUBLE_UNIT = 0x1.0p-53; 即 1.0 / (1L << 53)
}
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生成的随机数并不是真正的随机数,而是伪随机数。真正的随机数在计算机程序中很难产生。伪随机数基于一个种子seed,每需要一个随机数,都是对当前种子进行一些数学运算,得到一个数,基于该数得到需要的随机数和新的种子。
Random
在Random类中,有一个AtomicLong类型的属性seed;而且Random有其对应的构造方法。 从以下代码可以看出,只要seed相同,Random实例就一致,见Test2。
当构造Random不指定seed时,会自动得到一个真正随机的seed(与系统当前时间纳秒数相关)。
//Random.class
private final AtomicLong seed;
private static final long multiplier = 0x5DEECE66DL;
private static final long mask = (1L << 48) - 1; //即0xFFFFFFFFFFFF
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
private static long initialScramble(long seed) {
return (seed ^ multiplier) & mask;
}
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
//在JVM生命周期内,每次得到的seedUniquifier都不会相同;在不同的JVM内,因为还与系统的纳秒时间进行了抑或操作,得到的seed也不会相同。
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);
//Test2
Random random1 = new Random(12345678L);
Random random2 = new Random(12345678L);
for(int i = 0; i < 5; i++) {
System.out.print(random1.nextInt(100) + " ");
}
System.out.println();
for(int i = 0; i < 5; i++) {
System.out.print(random2.nextInt(100) + " ");
}
结果为:
63 2 85 71 45
63 2 85 71 45
随机举例
简单随机密码
class RandomPassword {
static final String SPECIAL_CHARS = "!@#$%^&*_+=-/";
static final int LENGTH = SPECIAL_CHARS.length();
String getPassWord(int length) {
Random rnd = new Random();
char[] chars = new char[length];
for (int i = 0; i < length; i++) {
int random = rnd.nextInt(20); // 此处带了一定的权重,特殊字符出现的概率控制在10%
if (random < 6) {
chars[i] = (char) ('a' + rnd.nextInt(26));
} else if (random < 12) {
chars[i] = (char) ('A' + rnd.nextInt(26));
} else if (random < 18) {
chars[i] = (char) ('0' + rnd.nextInt(10));
} else {
chars[i] = SPECIAL_CHARS.charAt(rnd.nextInt(LENGTH));
}
}
return new String(chars);
}
}
测试:
RandomPassword randomPassword = new RandomPassword();
for(int i = 0; i < 5; i++) {
System.out.println(randomPassword.getPassWord(8 + i));
}
结果:
1=SgkW/2
5XJc462ZD
6dPUO!x913
lTKpS2v4QuP
AfCwhhP0T9S@
洗牌
class Shuffle {
static void shuffle(int[] arr) {
Random rnd = new Random();
int len = arr.length;
for (int i = 0; i < len; i++) {
int exchange = rnd.nextInt(len);
//交换第i个元素与随机一个元素
int temp = arr[i];
arr[i] = arr[exchange];
arr[exchange] = temp;
}
}
}
int[] array = new int[20];
for (int i = 0; i < 20;) {
array[i] = ++i;
}
System.out.println(Arrays.toString(array));
Shuffle.shuffle(array);
System.out.println(Arrays.toString(array));
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[7, 2, 13, 12, 1, 9, 19, 3, 16, 11, 4, 14, 5, 17, 8, 6, 18, 10, 15, 20]