美文网首页
真/伪随机、以及随机算法

真/伪随机、以及随机算法

作者: 殷俊杰 | 来源:发表于2018-09-06 11:40 被阅读0次

    伪随机性(英语:Pseudorandomness)是一个过程似乎是随机的,但实际上并不是。伪随机数是看似随机实质是固定的周期性序列,也就是有规则的随机。
    什么是随机数
    随机数在计算机应用中使用的比较广泛,最为熟知的便是在密码学中的应用。随机数有3个特性,具体如下:

    随机性:不存在统计学偏差,是完全杂乱的数列
    不可预测性:不能从过去的数列推测出下一个出现的数
    不可重现性:除非将数列本身保存下来,否则不能重现相同的数列

    Random算法
    Random的使用是把要随机的集合顺序排列,从集合中随机挑选
    Random详细用法请看我这篇文章Java中Random的用法
    Shuffle算法
    Shuffle算法和排序算法正好相反,是从有序到乱序的一个过程,俗称洗牌算法。
    在Java中,有现成的shuffle算法实现,即Collections类中的两个重载的shuffle方法:

    public static void shuffle(List<?> list) {
        Random rnd = r;
        if (rnd == null)
            r = rnd = new Random();
        shuffle(list, rnd);
    }
    private static Random r;
    
    public static void shuffle(List<?> list, Random rnd) {
        int size = list.size();
        if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=size; i>1; i--)
                swap(list, i-1, rnd.nextInt(i));
        } else {
            Object arr[] = list.toArray();
    
            // Shuffle array
            for (int i=size; i>1; i--)
                swap(arr, i-1, rnd.nextInt(i));
    
            // Dump array back into list
            ListIterator it = list.listIterator();
            for (int i=0; i<arr.length; i++) {
                it.next();
                it.set(arr[i]);
            }
        }
    }
    

    真随机与伪随机
    随机数分为真随机数和伪随机数,我们程序使用的基本都是伪随机数,其中伪随机又分为强伪随机数和弱伪随机数。

    真随机数,通过物理实验得出,比如掷钱币、骰子、转轮、使用电子元件的噪音、核裂变等。需要满足随机性、不可预测性、不可重现性。
    伪随机数,通过一定算法和种子得出。软件实现的是伪随机数。
    强伪随机数,难以预测的随机数。需要满足随机性和不可预测性。
    弱伪随机数,易于预测的随机数。需要满足随机性。
    上面介绍Random算法和Shuffle算法的时候,代码实现都是伪随机算法。可以这样说:

    只要这个随机数是由确定算法生成的,那就是伪随机。只能通过不断算法优化,使你的随机数更接近随机。

    有限状态机不能产生真正的随机数的,所以,现代计算机中,无法通过一个纯算法来生成真正的随机数,无论是哪种语言,单纯的算法生成的数字都是伪随机数,都是由可确定的函数通过一个种子,产生的伪随机数。

    这也就意味着,如果知道了种子,就可以推断接下来的随机数序列的信息。这就有了可预测性。

    那么真随机数怎么产生的呢?

    通过真实随机事件取得的随机数才是真随机数。

    真正的随机数是使用物理现象产生的:比如掷钱币、骰子、转轮、使用电子元件的噪音、核裂变等等。这样的随机数发生器叫做物理性随机数发生器,它们的缺点是技术要求比较高,效率低。

    现有的真随机数生成器,比如PuTTYgen的随机数是让用户移动鼠标达到一定的长度,之后把鼠标的运动轨迹转化为种子;Intel通过电阻和振荡器来生成热噪声作为信息熵资源;Unix/Linux的dev/random和/dev/urandom采用硬件噪音生成随机数;

    所以,要想生成真的随机数,是无法用任何一个纯算法实现的。都需要借助外部物理现象。

    Java中的随机数生成器
    Java语言提供了几种随机数生成器,如前面提到的Random类,还有SecureRandom类。

    伪随机数生成器

    伪随机数发生器采用特定的算法,将随机数种子seed转换成一系列的伪随机数。伪随机数依赖于seed的值,给定相同的seed值总是生成相同的随机数。伪随机数的生成过程只依赖CPU,不依赖任何外部设备,生成速度快,不会阻塞。

    Java提供的伪随机数发生器有java.util.Random类和java.util.concurrent.ThreadLocalRandom类。

    Random类采用AtomicLong实现,保证多线程的线程安全性,但正如该类注释上说明的,多线程并发获取随机数时性能较差。

    多线程环境中可以使用ThreadLocalRandom作为随机数发生器,ThreadLocalRandom采用了线程局部变量来改善性能,这样就可以使用long而不是AtomicLong,此外,ThreadLocalRandom还进行了字节填充,以避免伪共享。

    强随机数发生器

    强随机数发生器依赖于操作系统底层提供的随机事件。强随机数生成器的初始化速度和生成速度都较慢,而且由于需要一定的熵累积才能生成足够强度的随机数,所以可能会造成阻塞。熵累积通常来源于多个随机事件源,如敲击键盘的时间间隔,移动鼠标的距离与间隔,特定中断的时间间隔等。所以,只有在需要生成加密性强的随机数据的时候才用它。

    Java提供的强随机数发生器是java.security.SecureRandom类,该类也是一个线程安全类,使用synchronize方法保证线程安全,但jdk并没有做出承诺在将来改变SecureRandom的线程安全性。因此,同Random一样,在高并发的多线程环境中可能会有性能问题。

    在linux的实现中,可以使用/dev/random和/dev/urandom作为随机事件源。由于/dev/random是堵塞的,在读取随机数的时候,当熵池值为空的时候会堵塞影响性能,尤其是系统大并发的生成随机数的时候。

    真随机数发生器

    在Linux系统中,SecureRandom的实现借助了/dev/random和/dev/urandom,可以使用硬件噪音生成随机数;

    http://random.org/,从1998年开始提供在线真随机数服务了,它用大气噪音生成真随机数。他也提供了Java工具类,可以拿来使用。地址:https://sourceforge.net/projects/randomjapi/

    相关文章

      网友评论

          本文标题:真/伪随机、以及随机算法

          本文链接:https://www.haomeiwen.com/subject/cfotgftx.html