关于随机数的问题,大家一起学习。
-----------------------------------------------------------------------------
首先关于随机数,随机数一般分为两种,真随机数和伪随机数。真随机一般是指统计意义上的随机,真正的随机
数是使用物理现象产生的,比如抛硬币、抛色子、转轮以及电子元件的噪音、宇宙射线、核裂变、核聚变等,真正的
随机数是在实验过程中产生的,其结果是不可预见的,是在某一定区域内随机的分布的。理论上来说,计算机系统也
可以产生真随机数(比如人在操作计算机时,鼠标的移动,键盘的敲击都是不可预测,外界命令导致计算机硬件的中
断、执行的进程,加载的数据也是不可预测,采集这些数据作为种子,那么产生的随机数也是不可预测的,只不过要
消耗较大的资源,速度有点慢,这就有点不划算了),但到目前为止计算机产生的都是伪随机数,因为计算机中的随
机数是按照一定算法模拟产生的,其结果在一定范围内是可预见的,伪随机数是可以在相同的条件下重复产生的。在
通常情况下,可以使用随机数发生器产生重复率极小的数字,这会让我们误以为它是真随机数。
一般来说,在计算机中伪随机数的产生有很多种方法。
有正态分布、二项式分布、泊松分布、线性同余法等等,其中线性同余法是最常用的。
具体对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)