Java随机数总结

内容主要参考
http://www.cnblogs.com/Fskjb/archive/2009/08/29/1556417.html
http://blog.sina.com.cn/s/blog_93dc666c0101h3gd.html
http://www.cnblogs.com/Ant-soldier/p/5500539.html
三篇博客。

先放一道面试真题

以下关于随机数的描述,正确的是:
A. Matn.random() 可以生成 [ 0 , 1 ] 内的任意小数
B. Random.next( 10 ) 可以生成 [ 0 , 10 ] 内的任意整数
C. new java.util.Random().nextInt( 11 ) 可以生成 [ 0 , 10 ] 内的任意整数
D. new java.util.Math().random() 可以生成 [ 0 , 1 ) 内的任意小数

在Java项目中通常主要是通过Math.random方法和Random类来获得随机数的,下面将从概述,使用技巧,源码分析等方面进行介绍。

一、Random类

1.概述

Random类中实现的随机算法是伪随机,也就是有规则的随机,实际上就是一个数字(seed)经过运算后近似随机数的数字。所以实际上伪随机是完全可以通过运算预测的。Java中的随机数种子若不填写就会使用缺省值,即系统时间。

2、Random对象的构造方法

a、public Random()

该构造方法使用一个和当前系统时间对应的相对时间有关的数字作为种子数,然后使用这个种子数构造Random对象。

b、public Random(long seed)

该构造方法可以通过给定的种子进行创建。
如下代码所示:

    public class Client {  
         public static void main(String[] args) {  
              Random r = new Random();  
          Random r1 = new Random(10);
              for(int i=1;i<4;i++){  
                System.out.println("第"+i+"次,r=:"+r.nextInt()+"r1=:"+r1.nextInt());  
            }  
         }  
    }

观察结果发现r1多次运行产生结果相同,r不同

3.Java.util.Random()类的方法

PS:什么是随机数生成器序列?类似一个数组,里面存着生成好的随机数,详见第三部分源码分析

  1. protected int next(int bits):生成下一个伪随机数,参数bits应该是位数。
  2. boolean nextBoolean():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的boolean值,生成true和false的值几率相等。
  3. void nextBytes(byte[] bytes):生成随机字节并将其置于用户提供的 byte 数组中。
  4. double nextDouble():返回下一个伪随机数,它是取自此随机数生成器序列的、在0.0和1.0之间均匀分布的 double值,不包括1.0
  5. float nextFloat():同上,但类型为Float
  6. double nextGaussian():返回下一个伪随机数,它是取自此随机数生成器序列的、呈高斯(“正态”)分布的double值,其平均值是0.0标准差是1.0。
  7. int nextInt():返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值,该值介于int的区间,也就是-231到231-1之间。
  8. int nextInt(int n):返回一个伪随机数,它是取自此随机数生成器序列的、该值介于[0,n)的区间,也就是0到n,不包括n。
  9. long nextLong():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 long 值。
  10. void setSeed(long seed):使用单个 long 种子设置此随机数生成器的种子。

常用的方法主要是2.4.7.8.10,记住这几个即可,下面介绍随机数的使用技巧。

4.使用示例

Random r = new Random();

a、生成[0,1.0)区间的小数
    double d1 = r.nextDouble();
    直接使用nextDouble方法获得。
b、生成[0,5.0)区间的小数

double d2 = r.nextDouble() * 5;
因为nextDouble方法生成的数字区间是[0,1.0),将该区间扩大5倍即是要求的区间。
同理,生成[0,d)区间的随机小数,d为任意正的小数,则只需要将nextDouble方法的返回值乘以d即可。

c、生成[1,2.5)区间的小数

double d3 = r.nextDouble() * 1.5 + 1;
生成[1,2.5)区间的随机小数,则只需要首先生成[0,1.5)区间的随机数字,然后将生成的随机数区间加1即可。
同理,生成任意非从0开始的小数区间[d1,d2)范围的随机数字(其中d1不等于0),则只需要首先生成[0,d2-d1)区间的随机数字,然后将生成的随机数字区间加上d1即可。

d、生成任意整数

int n1 = r.nextInt();
直接使用nextInt方法即可。

e、生成[0,10)区间的整数
n2 = Math.abs(r.nextInt() % 10);```
        以上两行代码均可生成[0,10)区间的整数。
        第一种实现使用Random类中的nextInt(int n)方法直接实现。
        第二种实现中,首先调用nextInt()方法生成一个任意的int数字,该数字和10取余以后生成的数字区间为(-10,10),然后再对该区间求绝对值,则得到的区间就是[0,10)了。
        同理,生成任意[0,n)区间的随机整数,都可以使用如下代码:
        ```int n2 = r.nextInt(n);
n2 = Math.abs(r.nextInt() % n);```
#####f、生成[0,10]区间的整数
        int n3 = r.nextInt(11);
        n3 = Math.abs(r.nextInt() % 11);
相对于整数区间,[0,10]区间和[0,11)区间等价,所以即生成[0,11)区间的整数。
#####g、生成[-3,15)区间的整数
        int n4 = r.nextInt(18) - 3;
        n4 = Math.abs(r.nextInt() % 18) - 3;
生成非从0开始区间的随机整数,可以参看上面非从0开始的小数区间实现原理的说明。
#####h、几率实现
按照一定的几率实现程序逻辑也是随机处理可以解决的一个问题。下面以一个简单的示例演示如何使用随机数字实现几率的逻辑。
        在前面的方法介绍中,nextInt(int n)方法中生成的数字是均匀的,也就是说该区间内部的每个数字生成的几率是相同的。那么如果生成一个[0,100)区间的随机整数,则每个数字生成的几率应该是相同的,而且由于该区间中总计有100个整数,所以每个数字的几率都是1%。按照这个理论,可以实现程序中的几率问题。
        示例:随机生成一个整数,该整数以55%的几率生成1,以40%的几率生成2,以5%的几率生成3。实现的代码如下:

                int n5 = r.nextInt(100);
        int m; //结果数字
        if(n5 < 55){ //55个数字的区间,55%的几率
        m = 1;
        }else if(n5 < 95){//[55,95),40个数字的区间,40%的几率
        m = 2;
        }else{
        m = 3;
        }```
因为每个数字的几率都是1%,则任意55个数字的区间的几率就是55%,为了代码方便书写,这里使用[0,55)区间的所有整数,后续的原理一样。
当然,这里的代码可以简化,因为几率都是5%的倍数,所以只要以5%为基础来控制几率即可,下面是简化的代码实现:
 
                              int n6 = r.nextInt(20);
                      int m1;
                      if(n6 < 11){
                               m1 = 1;
                      }else if(n6 < 19){
                               m1= 2;
                      }else{
                               m1 = 3;
                      }

在程序内部,几率的逻辑就可以按照上面的说明进行实现。

##二、java.lang.Math.Random方法

        
###1.使用
调用这个Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0,即取值范围是[0.0,1.0)的左闭右开区间,返回值是一个伪随机选择的数,在该范围内(近似)均匀分布。
例如下面的实验代码
编译通过后运行结果如下图

![](http://upload-images.jianshu.io/upload_images/4767614-e3aab895e94f709f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
观察会发现代码的用一个循环10次循环输出num的取值,均随机分布在[0,3)之间!在使用Math.Random()的时候需要注意的地方时该函数是返回double类型的值,所以在要赋值给其他类型的变量的时候注意需要进行塑形转换。
        
###2.总结
a.通过阅读Math类的源代码可以发现,Math类中的random方法就是直接调用Random类中的nextDouble方法实现的,两者并无差别。
        只是random方法的调用比较简单,所以很多程序员都习惯使用Math类的random方法来生成随机数字。

b.java.util.Random()在调用的时候可以实现和java.Math.Random()一样的功能,而且他具有很多的调用方法,相对来说比较灵活。所以从总体来看,使用java.util.Random()会相对来说比较灵活一些。

C.java.Math.Random()实际是在内部调用java.util.Random()的,它有一个致命的弱点,它和系统时间有关,也就是说相隔时间很短的两个random比如:
        double a = Math.random();
        double b = Math.random();
        即有可能会得到两个一模一样的double。

d。在使用random类时,如果想避免出现随机数字相同的情况,则需要注意,无论项目中需要生成多少个随机数字,都只使用一个Random对象即可。
        
##三、源码分析
其实我也没有看太懂……先把这个博客上的内容贴过来,等弄懂了再补自己的解释吧……
    来自 <http://www.cnblogs.com/Ant-soldier/p/5500539.html> 
        * @param seed the initial seed
        * @see #setSeed(long)
        */
        ++++++++++++++++++带种子数的构造方法+++++++++++++
        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);
        }
        }
         
        ++++++++++++++netInt方法带参数的那个源码++++++++++++
        * @since 1.2
        */
        public int nextInt(int n) {
        if (n <= 0)
        throw new IllegalArgumentException("n must be positive");
        if ((n & -n) == n) // i.e., n is a power of 2
        return (int)((n * (long)next(31)) >> 31);
        int bits, val;
        do {
        bits = next(31);
        val = bits % n;
        } while (bits - val + (n-1) < 0);
        return val;
        }
        可见Random的种子要求 大于0 的 。。。
        +++++++++++++++nextDouble方法实现+++++++++++
        public double nextDouble() {
        return (((long)(next(26)) << 27) + next(27))
        / (double)(1L << 53);
        }
        +++++++++++++++nextFloat方法实现+++++++++++++
        public float nextFloat() {
        return next(24) / ((float)(1 << 24));
        }
         
        +++++++++++++++++nextInt方法实现:++++++++++
        public int nextInt() {
        return next(32);
        }
         
        可见所有的随机数产生都和一个叫 next方法有关,这个方法是这样的:
        * @since 1.1
        */
        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));
        } 
        一般计算机的随机数都是伪随机数,以一个真随机数(种子)作为初始条件,然后用一定的算法不停迭代产生随机数,下面介绍两种方法: 
        
        算法1:平方取中法。 
        1)将种子设为X0,并mod 10000得到4位数 
         
        2)将它平方得到一个8位数(不足8位时前面补0) 
         
        3)取中间的4位数可得到下一个4位随机数X1 
         
        4)重复1-3步,即可产生多个随机数 
         
        这个算法的一个主要缺点是最终它会退化成0,不能继续产生随机数。 
        
        算法2:线性同余法 
         
        1)将种子设为X0, 
         
        2)用一个算法X(n+1)=(a*X(n)+b) mod c产生X(n+1) 
         
        一般将c取得很大,可产生0到c-1之间的伪随机数 
        该算法的一个缺点是会出现循环。
        
        
        下面这个就简单啦。
        Math类的源代码,Math类中的random方法就是直接调用Random类中的nextDouble方法实现的。
        * @see Random#nextDouble()
        */
        public static double random() {
        Random rnd = randomNumberGenerator;
        if (rnd == null) rnd = initRNG();
        return rnd.nextDouble();
        }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 方法1 (数据类型)(最小值+Math.random()*(最大值-最小值+1)) 例: (int)(1+Math...
    GB_speak阅读 40,904评论 2 6
  • Random类 (java.util) Random类中实现的随机算法是伪随机,也就是有规则的随机。在进行随机时,...
    望月成三人阅读 431评论 0 0
  • Random 类作为JAVA中用于产生的随机数 ,new Random(10) :10是种子数。注意:Rand...
    成江阅读 763评论 0 0
  • 1 顺序语句 语句:使用分号分隔的代码称作为一个语句。 注意:没有写任何代码只是一个分号的时候,也是一条语句,...
    哈哈哎呦喂阅读 364评论 0 0
  • 怀旧是一场病,一病就是三年。 在这三年里,知暖拒绝了吃药,也拒绝了时间的安慰。犯病的她矛盾,多疑,且神经质,身边的...
    一记耳光阅读 244评论 1 6