关于java随机数的问题

 关于随机数的问题,大家一起学习。

-----------------------------------------------------------------------------

    首先关于随机数,随机数一般分为两种,真随机数和伪随机数。真随机一般是指统计意义上的随机,真正的随机

数是使用物理现象产生的,比如抛硬币、抛色子、转轮以及电子元件的噪音、宇宙射线、核裂变、核聚变等,真正的

随机数是在实验过程中产生的,其结果是不可预见的,是在某一定区域内随机的分布的。理论上来说,计算机系统也

可以产生真随机数(比如人在操作计算机时,鼠标的移动,键盘的敲击都是不可预测,外界命令导致计算机硬件的中

断、执行的进程,加载的数据也是不可预测,采集这些数据作为种子,那么产生的随机数也是不可预测的,只不过要

消耗较大的资源,速度有点慢,这就有点不划算了),但到目前为止计算机产生的都是伪随机数,因为计算机中的随

机数是按照一定算法模拟产生的,其结果在一定范围内是可预见的,伪随机数是可以在相同的条件下重复产生的。在

通常情况下,可以使用随机数发生器产生重复率极小的数字,这会让我们误以为它是真随机数。

    一般来说,在计算机中伪随机数的产生有很多种方法。

有正态分布、二项式分布、泊松分布、线性同余法等等,其中线性同余法是最常用的。

    具体对java而言,也有不同的实现方式。java中的java.util.Random类就是使用的线性同余法,可以在

myeclipse中查看到Random类的源代码,具体如下:


import java.io.*;
import java.util.concurrent.atomic.AtomicLong;
import sun.misc.Unsafe;

/**
 * An instance of this class is used to generate a stream of
 * pseudorandom numbers. The class uses a 48-bit seed, which is
 * modified using a linear congruential formula. (See Donald Knuth,
 * <i>The Art of Computer Programming, Volume 3</i>, Section 3.2.1.)
 */

    public
    class Random implements java.io.Serializable {
    /** use serialVersionUID from JDK 1.1 for interoperability */
    <span style="color:#FF0000;">static final long serialVersionUID = 3905348978240129619L;</span>
    private final AtomicLong seed;

    private fina l static long multiplier = 0x5DEECE66DL;
    private final static long addend = 0xBL;
    private final static long mask = <span style="color:#FF0000;">(1L << 48) - 1</span>;
    public Random() { 
        <span style="color:#FF0000;"> this(++seedUniquifier + System.nanoTime());</span> 
    }
    
   <span style="color:#FF0000;"> private static volatile long seedUniquifier = 8682522807148012L;</span>

 
    public Random(long seed) {
        this.seed = new AtomicLong(0L);
        setSeed(seed);
    }

    
    synchronized public void setSeed(long seed) {
        seed = (seed ^ multiplier) & mask;
        this.seed.set(seed);
    	haveNextNextGaussian = false;
    }

    
    public void nextBytes(byte[] bytes) {
	for (int i = 0, len = bytes.length; i < len; )
	    for (int rnd = nextInt(),
		     n = Math.min(len - i, Integer.SIZE/Byte.SIZE);
		 n-- > 0; rnd >>= Byte.SIZE)
		bytes[i++] = (byte)rnd;
    }

  
    public int nextInt() {
	return next(32);
    }

   

    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;
    }

   
    public long nextLong() {
        // it's okay that the bottom word remains signed.
        return ((long)(next(32)) << 32) + next(32);
    }

   
    public float nextFloat() {
        return next(24) / ((float)(1 << 24));
    }

   
    public double nextDouble() {
        return (((long)(next(26)) << 27) + next(27))
	    / (double)(1L << 53);
    }

    private double nextNextGaussian;
    private boolean haveNextNextGaussian = false;

    
    synchronized public double nextGaussian() {
        // See Knuth, ACP, Section 3.4.1 Algorithm C.
        if (haveNextNextGaussian) {
    	    haveNextNextGaussian = false;
    	    return nextNextGaussian;
    	} else {
            double v1, v2, s;
    	    do {
                v1 = 2 * nextDouble() - 1; // between -1 and 1
            	v2 = 2 * nextDouble() - 1; // between -1 and 1
                s = v1 * v1 + v2 * v2;
    	    } while (s >= 1 || s == 0);
    	    double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
    	    nextNextGaussian = v2 * multiplier;
    	    haveNextNextGaussian = true;
    	    return v1 * multiplier;
        }
    }


    private static final ObjectStreamField[] serialPersistentFields = {
        new ObjectStreamField("seed", Long.TYPE),
        new ObjectStreamField("nextNextGaussian", Double.TYPE),
        new ObjectStreamField("haveNextNextGaussian", Boolean.TYPE)
    };

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {

        ObjectInputStream.GetField fields = s.readFields();

	// The seed is read in as {@code long} for
	// historical reasons, but it is converted to an AtomicLong.
	long seedVal = (long) fields.get("seed", -1L);
        if (seedVal < 0)
          throw new java.io.StreamCorruptedException(
                              "Random: invalid seed");
        resetSeed(seedVal);
        nextNextGaussian = fields.get("nextNextGaussian", 0.0);
        haveNextNextGaussian = fields.get("haveNextNextGaussian", false);
    }

 
    synchronized private void writeObject(ObjectOutputStream s)
	throws IOException {

        // set the values of the Serializable fields
        ObjectOutputStream.PutField fields = s.putFields();

	// The seed is serialized as a long for historical reasons.
        fields.put("seed", seed.get());
        fields.put("nextNextGaussian", nextNextGaussian);
        fields.put("haveNextNextGaussian", haveNextNextGaussian);

        // save them
        s.writeFields();
    }

    // Support for resetting seed while deserializing
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long seedOffset;
    static {
        try {
            seedOffset = unsafe.objectFieldOffset
                (Random.class.getDeclaredField("seed"));
	} catch (Exception ex) { throw new Error(ex); }
    }
    private void resetSeed(long seedVal) {
        unsafe.putObjectVolatile(this, seedOffset, new AtomicLong(seedVal));
    }
}


<span style="font-family:SimSun;"><span style="font-family:SimSun;font-size:18px;">package java.util;
import java.io.*;
import java.util.concurrent.atomic.AtomicLong;
import sun.misc.Unsafe;

/**
 * An instance of this class is used to generate a stream of
 * pseudorandom numbers. The class uses a 48-bit seed, which is
 * modified using a linear congruential formula. (See Donald Knuth,
 * <i>The Art of Computer Programming, Volume 3</i>, Section 3.2.1.)
 */

    public
    class Random implements java.io.Serializable {
    /** use serialVersionUID from JDK 1.1 for interoperability */
    <span style="color:#FF0000;">static final long serialVersionUID = 3905348978240129619L;</span>
    private final AtomicLong seed;

    private fina l static long multiplier = 0x5DEECE66DL;
    private final static long addend = 0xBL;
    private final static long mask = <span style="color:#FF0000;">(1L << 48) - 1</span>;
    public Random() { 
        <span style="color:#FF0000;"> this(++seedUniquifier + System.nanoTime());</span> 
    }
    
   <span style="color:#FF0000;"> private static volatile long seedUniquifier = 8682522807148012L;</span>

 
    public Random(long seed) {
        this.seed = new AtomicLong(0L);
        setSeed(seed);
    }

    
    synchronized public void setSeed(long seed) {
        seed = (seed ^ multiplier) & mask;
        this.seed.set(seed);
    	haveNextNextGaussian = false;
    }

    
    public void nextBytes(byte[] bytes) {
	for (int i = 0, len = bytes.length; i < len; )
	    for (int rnd = nextInt(),
		     n = Math.min(len - i, Integer.SIZE/Byte.SIZE);
		 n-- > 0; rnd >>= Byte.SIZE)
		bytes[i++] = (byte)rnd;
    }

  
    public int nextInt() {
	return next(32);
    }

   

    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;
    }

   
    public long nextLong() {
        // it's okay that the bottom word remains signed.
        return ((long)(next(32)) << 32) + next(32);
    }

   
    public float nextFloat() {
        return next(24) / ((float)(1 << 24));
    }

   
    public double nextDouble() {
        return (((long)(next(26)) << 27) + next(27))
	    / (double)(1L << 53);
    }

    private double nextNextGaussian;
    private boolean haveNextNextGaussian = false;

    
    synchronized public double nextGaussian() {
        // See Knuth, ACP, Section 3.4.1 Algorithm C.
        if (haveNextNextGaussian) {
    	    haveNextNextGaussian = false;
    	    return nextNextGaussian;
    	} else {
            double v1, v2, s;
    	    do {
                v1 = 2 * nextDouble() - 1; // between -1 and 1
            	v2 = 2 * nextDouble() - 1; // between -1 and 1
                s = v1 * v1 + v2 * v2;
    	    } while (s >= 1 || s == 0);
    	    double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
    	    nextNextGaussian = v2 * multiplier;
    	    haveNextNextGaussian = true;
    	    return v1 * multiplier;
        }
    }


    private static final ObjectStreamField[] serialPersistentFields = {
        new ObjectStreamField("seed", Long.TYPE),
        new ObjectStreamField("nextNextGaussian", Double.TYPE),
        new ObjectStreamField("haveNextNextGaussian", Boolean.TYPE)
    };

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {

        ObjectInputStream.GetField fields = s.readFields();

	// The seed is read in as {@code long} for
	// historical reasons, but it is converted to an AtomicLong.
	long seedVal = (long) fields.get("seed", -1L);
        if (seedVal < 0)
          throw new java.io.StreamCorruptedException(
                              "Random: invalid seed");
        resetSeed(seedVal);
        nextNextGaussian = fields.get("nextNextGaussian", 0.0);
        haveNextNextGaussian = fields.get("haveNextNextGaussian", false);
    }

 
    synchronized private void writeObject(ObjectOutputStream s)
	throws IOException {

        // set the values of the Serializable fields
        ObjectOutputStream.PutField fields = s.putFields();

	// The seed is serialized as a long for historical reasons.
        fields.put("seed", seed.get());
        fields.put("nextNextGaussian", nextNextGaussian);
        fields.put("haveNextNextGaussian", haveNextNextGaussian);

        // save them
        s.writeFields();
    }

    // Support for resetting seed while deserializing
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long seedOffset;
    static {
        try {
            seedOffset = unsafe.objectFieldOffset
                (Random.class.getDeclaredField("seed"));
	} catch (Exception ex) { throw new Error(ex); }
    }
    private void resetSeed(long seedVal) {
        unsafe.putObjectVolatile(this, seedOffset, new AtomicLong(seedVal));
    }
}
</span></span>

Random使用了一个System.nanoTime()方法来获得一个纳秒级的时间量,参与48粒种子的构成。这段代码过程如下

1.获得一个长整型作为初始种子默认值是3905348978240129619L

2.不断的与8682522807148012L,直到某一次相乘前后结果相同

3.与系统随机出来的nanotime值作异或运算,得到最终的种子

具体应用我们可以看这个例子:



<span style="font-family:SimSun;"><span style="font-family:SimSun;font-size:18px;">import java.util.Random;

public class as{

public static void main(String[] args) {

// 使用java.lang.Math的random方法生成0到1之间的随机数
System.out.println("Math.random(): " + Math.random());

// 使用不带参数的构造方法构造java.util.Random对象
System.err.println("使用不带参数的构造方法构造的Random对象:");
Random rd1 = new Random();

// 按均匀分布产生整数
System.out.println("int: " + rd1.nextInt());
// 按均匀分布产生长整数
System.out.println("long: " + rd1.nextLong());
// 按均匀分布产生大于等于0,小于1的float数[0, 1)
System.out.println("float: " + rd1.nextFloat());
// 按均匀分布产生[0, 1)范围的double数
System.out.println("double: " + rd1.nextDouble());
// 指定随机数产生的范围
System.out.print("[2,10)范围内随机整数序列: ");
for (int i = 0; i < 10; i++) {
	
System.out.print(2+rd1.nextInt(10) + " ");//nextInt(int n)方法的范围是从0开始的
}


// 使用带参数的构造方法构造Random对象
// 构造函数的参数是long类型,是生成随机数的种子。
System.out.println("\n\n");
System.out.println("使用带参数的构造方法构造的Random对象:");

<pre name="code" class="java">// 对于种子相同的Random对象,生成的随机数序列是一样的。
<span style="font-family:SimSun;font-size:18px;">Random ran2 = new Random(10);System.out.println("使用种子为10的Random对象生成[0,10)内随机整数序列: ");
for (int i = 0; i < 10; i++) {
    System.out.print(ran2.nextInt(10) + " ");
}

Random ran3 = new Random(10);
System.out.println("使用种子为10的Random对象生成[0,10)内随机整数序列: ");
for (int i = 0; i < 10; i++) {
  System.out.print(ran3.nextInt(10) + " ");
}
System.out.println();// ran2和ran3生成的随机数序列是一样的,
</span></span><p><span style="font-family:SimSun;font-size:18px;">//这是因为在没带参数构造函数生成的Random对象的种子缺省是当前系统时间的毫秒数。</span></p><p><span style="font-family:SimSun;font-size:18px;">//直接使用Random无法避免生成重复的数字,如果需要生成不重复的随机数序列,需要借助数组和集合类</span></p><p><span style="font-family:SimSun;font-size:18px;"> }}</span></p>
</span>




关于java.util.Random()是一个对象。它的方法必须是通过一个实例来调用,这意味着必须先调用它的构造函数。如

果在内存充足的情况下,没有什么问题,但如果内存不足时,就会带来问题。可以使用一个静态类java.lang.Math来

解决。

Math.random()是Random.nextDouble()的一个内部方法.Random.nextDouble()使用Random.next()两次,均匀的分布范围为0到1 - (2 ^ -53)。

如要产生0.0和10.0之间的双精度浮点数会这样来写:

Math.random() * 10

而产生0和10之间的整数,则会写成:

Math.round(Math.random() * 10)





  

  



   



 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值