zoukankan      html  css  js  c++  java
  • Java中随机数的产生方式与原理

    查阅随机数相关资料,特做整理

    首先说一下java中产生随机数的几种方式

    1. 在j2se中我们可以使用Math.random()方法来产生一个随机数,这个产生的随机数是0-1之间的一个double,我们可以把他乘以100,他就是个100以内的随机数字,这个在j2me中没有。
    2. 在java.util这个包里面提供了一个Random的类,我们可以新建一个Random的对象来产生随机数,他可以生产随机整数、随机float、随机double、随机long,这个也是我们在j2me的程序里经常用的一个取随机数的方法。
    3. 在我们的System类中有一个currentTimeMillis()方法,这个方法返回一个从1970年1月1号0点0分0秒到目前的一个毫秒数,返回类型是long,我们可以拿他作为一个随机数,我们可以拿他对一些数取模,就可以把他限制在一个范围之内啦。

    EN。。。其实在Random的默认构造方法里也是使用上面第三种方法进行随机数的产生的。

    对于方法二中的Random类有两种构建方式:带种子和不带种子

    • 不带种子:此种方式将会返回随机的数字,每次运行结果不一样,相当于用System.currentTimeMillis()作种子。
    • 带种子:此种方式,无论程序运行多少次,返回结果都是一样的。如果用相同的种子创建两个Random实例,则对每个实例进行相同的方法调用序列,它们将生成并返回相同的数字序列。

    伪随机数

    计算机中的随机数都是伪随机数

    下面看这样一个C程序:

    //  rand_1.cpp
    #include <stdlib.h>
    static unsigned int RAND_SEED;
    unsigned int random(void)
    {
        RAND_SEED = (RAND_SEED*123+59)%65536;
        return (RAND_SEED);
    }
    void random_start(void)
    {
        int temp[2];
        movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
        RAND_SEED = temp[0];
    }
    void main()
    {
        unsigned int i,n;
        random_start();
        for(i=0;i<10;i++)
              printf("#u	",random());
        printf("
    ");
    }

    它完整地阐述了随机数产生的过程:
    首先,

    movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
    这个函数用来移动内存数据,其中FP_SEG(far pointer to segment)是取temp数组段地址的函数,FP_OFF(far pointer to offset)是取temp数组相对地址的函数,movedata函数的作用是把位于0040:006CH存储单元中的双字放到数组temp的声明的两个存储单元中。这样可以通过temp数组把0040:006CH处的一个16位的数送给RAND_SEED。

    其次,

    RAND_SEED=(RAND_SEED*123+59)%65536;
    是用来计算随机数的方法,随机数的计算方法在不同的计算机中是不同的,即使在相同的计算机中安装的不同的操作系统中也是不同的。我在linux和windows下分别试过,相同的随机种子在这两种操作系统中生成的随机数是不同的,这说明它们的计算方法不同。

    然后,

    movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
    随机种子为什么要在内存的0040:006CH处取?0040:006CH处存放的是什么?

    学过《计算机组成原理与接口技术》这门课的人可能会记得在编制ROM BIOS时钟中断服务程序时会用到Intel 8253定时/计数器,它与Intel 8259中断芯片的通信使得中断服务程序得以运转,主板每秒产生的18.2次中断正是处理器根据定时/记数器值控制中断芯片产生的。在我们计算机的主机板上都会有这样一个定时/记数器用来计算当前系统时间,每过一个时钟信号周期都会使记数器加一,而这个记数器的值存放在哪儿呢?没错,就在内存的0040:006CH处,其实这一段内存空间是这样定义的:

    TIMER_LOW DW ? ;地址为 0040:006CH
    TIMER_HIGH DW ? ;地址为 0040:006EH
    TIMER_OFT DB ? ;地址为 0040:0070H

    时钟中断服务程序中,每当TIMER_LOW转满时,此时,记数器也会转满,记数器的值归零,即TIMER_LOW处的16位二进制归零,而TIMER_HIGH加一。rand01.c中的

    movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);

    正是把TIMER_LOW和TIMER_HIGH两个16位二进制数放进temp数组,再送往RAND_SEED,从而获得了“随机种子”。
    现在,可以确定的一点是,随机种子来自系统时钟,确切地说,是来自计算机主板上的定时/计数器在内存中的记数值。

    EN...没有最后。。lvl--
    再看一段代码:

    //rand_2.cpp
    #include <iostream>
    #include <cstdlib>
    using namespace std;
    int main()
    {
        srand((unsigned)time(NULL));
        unsigned int r=rand();
        cout<<"r = "<<r<<endl; //根据C++ 98标准,可以不用return语句来介绍main函数
        return 0;
    }
    这里用户和其他程序没有设定随机种子,则使用系统定时/计数器的值做为随机种子,所以,在相同的平台环境下,编译生成exe后,每次运行它,显示的随机数会是伪随机数,即每次运行显示的结果会有不同。

    总结

    • 随机数是由随机种子根据一定的计算方法计算出来的数值。所以,只要计算方法一定,随机种子一定,那么产生的随机数就不会变。在相同的平台环境下,编译生成exe后,每次运行它,显示的随机数都是一样的。这是因为在相同的编译平台环境下,由随机种子生成随机数的计算方法都是一样的,再加上随机种子一样,所以产生的随机数就是一样的。

    • 只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统时钟(即定时/计数器的值)

  • 相关阅读:
    Codeforces Round #344 (Div. 2) C. Report 其他
    Codeforces Round #344 (Div. 2) B. Print Check 水题
    Codeforces Round #344 (Div. 2) A. Interview 水题
    8VC Venture Cup 2016
    CDOJ 1280 772002画马尾 每周一题 div1 矩阵快速幂 中二版
    CDOJ 1280 772002画马尾 每周一题 div1 矩阵快速幂
    CDOJ 1279 班委选举 每周一题 div2 暴力
    每周算法讲堂 快速幂
    8VC Venture Cup 2016
    Educational Codeforces Round 9 F. Magic Matrix 最小生成树
  • 原文地址:https://www.cnblogs.com/xieweikai/p/6817703.html
Copyright © 2011-2022 走看看