随机-Random

认识随机

// 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]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值