2012-04-18 29 views
7

Tôi đang sử dụng lớp Random trong Java dưới dạng trình tạo số giả ngẫu nhiên. Tôi đang sử dụng chức năng nextDouble rất nhiều lần (~ 10^5). Bao nhiêu lần trước khi tôi phải reseed để ngăn chặn từ việc nhận được cùng một số? Việc nhập lại có cần thiết không?Có bao nhiêu lần tôi có thể sử dụng randomGenerator.nextDouble() trước khi tôi cần phải gieo hạt lại?

Random generator = new Random(); 
    double[] numbers = new double[n]; 
    for (int i = 0; i < n; i++) numbers[i] = generator.nextDouble(); 

Đây là một thử nghiệm, số sẽ được sử dụng làm tọa độ cho điểm trên không gian, vì vậy tôi muốn phân phối càng thống nhất càng tốt.

Ngoài ra, làm thế nào để tôi được gieo hạt? Tôi lấy hạt giống từ đâu?

+0

Bạn đã xem http://stackoverflow.com/questions/7291911/ chưa? –

+2

Nói chung, một trình tạo số giả ngẫu nhiên tốt sẽ quay vòng qua toàn bộ các số nguyên mà nó đại diện trước khi lặp lại. Ngẫu nhiên xuất hiện để có một số nguyên nội bộ của 48 bit hiệu quả. –

+0

http://stackoverflow.com/questions/453479/how-good-is-java-util-random – Cratylus

Trả lời

5

Trình tạo số ngẫu nhiên sẽ tạo ngẫu nhiên gấp đôi từ hai giá trị int ngẫu nhiên. Hạt bên trong có 48 bit, do đó chuỗi ngẫu nhiên lặp lại sau nhiều nhất là 2^48 giá trị int, hoặc 2^47 giá trị kép.

+0

+1: Nếu bạn sử dụng SecureRandom, bạn không cần phải chèn sẵn. –

0

Tôi rất tiếc, tôi không thể trả lời trực tiếp câu hỏi của bạn. Tôi không nhớ thời gian chu kỳ của bộ tạo số ngẫu nhiên của Java. Mặc dù tôi nghĩ rằng bạn đang cắt nó gần với số lượng các số bạn đang tạo ra.

Nhưng, những gì tôi đã học được trong các lớp thống kê kỹ thuật máy tính của tôi có thể giúp bạn.

Tôi đã học được rằng phương pháp tốt nhất để tạo ra các số ngẫu nhiên nhất là sử dụng trình tạo số ngẫu nhiên Mersenne Twister. máy phát điện này sẽ cung cấp cho bạn đủ số ngẫu nhiên để không cần phải gieo hạt, nó có một khoảng thời gian (2^19.937) - 1

Đây là mã nguồn cho MerseeneTwister

https://java2s.com/Open-Source/Java/Natural-Language-Processing/MorphAdorner/edu/northwestern/at/utils/math/randomnumbers/MersenneTwister.java.htm

Dưới đây là một lớp học để tạo số ngẫu nhiên của bạn.

class RandomVariable { 

/** Initialize Mersenne Twister generator. */ 
private static MersenneTwister rnd = new MersenneTwister(); 

public static double rand() { 
    return rnd.nextDouble(); 
} 

/** Generate a random number from a uniform random variable. 
* 
* @param min Mininum value for the random variable. 
* @param max Maximum value for the random variable. 
* 
* @return  A random double between min and max. 
*/ 
public static double uniform(double min, double max) { 
    return min + (max - min) * rand(); 
} 
} 

Đây là mẫu để tạo số ngẫu nhiên. Xin lưu ý rằng tôi đã xóa nhận xét khỏi nguồn. Điều này có thể làm nổi bật bản chất nguồn mở của mã, nhưng tôi không thể sao chép tất cả và mã hóa nó thành mã.

import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.Serializable; 

public class sample{ 
public static void main(String args[]){ 
    RandomVariable gen = new RandomVariable(); 
    double num = gen.uniform(-1,1); 

    int n = 10000; 
    Set<Double> nums = new HashSet<Double>(); 
    while (numbers.size() < n) 
     nums.add(gen.uniform(-1,1)); 

} 
} 
class RandomVariable { 

/** Initialize Mersenne Twister generator. */ 
private static MersenneTwister rnd = new MersenneTwister(); 

public static double rand() { 
    return rnd.nextDouble(); 
} 

/** Generate a random number from a uniform random variable. 
* 
* @param min Mininum value for the random variable. 
* @param max Maximum value for the random variable. 
* 
* @return  A random double between min and max. 
*/ 
public static double uniform(double min, double max) { 
    return min + (max - min) * rand(); 
} 
} 

class MersenneTwister extends java.util.Random implements Serializable { 
// Period parameters 

private static final int N = 624; 
private static final int M = 397; 
private static final int MATRIX_A = 0x9908b0df; // private static final 
//* constant vector a 
private static final int UPPER_MASK = 0x80000000; // most significant 
// w-r bits 
private static final int LOWER_MASK = 0x7fffffff; // least significant 
// r bits 
// Tempering parameters 
private static final int TEMPERING_MASK_B = 0x9d2c5680; 
private static final int TEMPERING_MASK_C = 0xefc60000; 
private int mt[]; // the array for the state vector 
private int mti; // mti==N+1 means mt[N] is not initialized 
private int mag01[]; 
// a good initial seed (of int size, though stored in a long) 
// private static final long GOOD_SEED = 4357; 

/* implemented here because there's a bug in Random's implementation 
of the Gaussian code (divide by zero, and log(0), ugh!), yet its 
gaussian variables are private so we can't access them here. :-(*/ 
private double __nextNextGaussian; 
private boolean __haveNextNextGaussian; 

/** 
* Constructor using the default seed. 
*/ 
public MersenneTwister() { 
    this(System.currentTimeMillis()); 
} 

/** 
* Constructor using a given seed. Though you pass this seed in 
* as a long, it's best to make sure it's actually an integer. 
*/ 
public MersenneTwister(final long seed) { 
    super(seed); /* just in case */ 
    setSeed(seed); 
} 

/** 
* Constructor using an array. 
*/ 
public MersenneTwister(final int[] array) { 
    super(System.currentTimeMillis()); 
    /* pick something at random just in case */ 
    setSeed(array); 
} 

/** 
* Initalize the pseudo random number generator. Don't 
* pass in a long that's bigger than an int (Mersenne Twister 
* only uses the first 32 bits for its seed). 
*/ 
synchronized public void setSeed(final long seed) { 
    // it's always good style to call super 
    super.setSeed(seed); 

    // Due to a bug in java.util.Random clear up to 1.2, we're 
    // doing our own Gaussian variable. 
    __haveNextNextGaussian = false; 

    mt = new int[N]; 

    mag01 = new int[2]; 
    mag01[0] = 0x0; 
    mag01[1] = MATRIX_A; 

    mt[0] = (int) (seed & 0xfffffff); 
    for (mti = 1; mti < N; mti++) { 
     mt[mti] = 
       (1812433253 * (mt[mti - 1]^(mt[mti - 1] >>> 30)) + mti); 

     /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ 

     /* In the previous versions, MSBs of the seed affect */ 

     /* only MSBs of the array mt[].      */ 

     /* 2002/01/09 modified by Makoto Matsumoto    */ 
     mt[mti] &= 0xffffffff; 

     /* for >32 bit machines */ 
    } 
} 

/** 
* An alternative, more complete, method of seeding the 
* pseudo random number generator. array must be an 
* array of 624 ints, and they can be any value as long as 
* they're not *all* zero. 
*/ 
synchronized public void setSeed(final int[] array) { 
    int i, j, k; 

    setSeed(19650218); 
    i = 1; 
    j = 0; 
    k = (N > array.length ? N : array.length); 
    for (; k != 0; k--) { 
     mt[i] = (mt[i]^((mt[i - 1]^(mt[i - 1] >>> 30)) * 1664525)) 
       + array[j] + j; /* non linear */ 
     mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */ 
     i++; 
     j++; 
     if (i >= N) { 
      mt[0] = mt[N - 1]; 
      i = 1; 
     } 
     if (j >= array.length) { 
      j = 0; 
     } 
    } 
    for (k = N - 1; k != 0; k--) { 
     mt[i] = (mt[i]^((mt[i - 1]^(mt[i - 1] >>> 30)) * 1566083941)) 
       - i; /* non linear */ 
     mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */ 
     i++; 
     if (i >= N) { 
      mt[0] = mt[N - 1]; 
      i = 1; 
     } 
    } 
    mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ 
} 

/** 
* Returns an integer with <em>bits</em> bits filled with a random number. 
*/ 
synchronized protected int next(final int bits) { 
    int y; 

    if (mti >= N) // generate N words at one time 
    { 
     int kk; 
     final int[] mt = this.mt; // locals are slightly faster 
     final int[] mag01 = this.mag01; // locals are slightly faster 

     for (kk = 0; kk < N - M; kk++) { 
      y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 
      mt[kk] = mt[kk + M]^(y >>> 1)^mag01[y & 0x1]; 
     } 
     for (; kk < N - 1; kk++) { 
      y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 
      mt[kk] = mt[kk + (M - N)]^(y >>> 1)^mag01[y & 0x1]; 
     } 
     y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); 
     mt[N - 1] = mt[M - 1]^(y >>> 1)^mag01[y & 0x1]; 

     mti = 0; 
    } 

    y = mt[mti++]; 
    y ^= y >>> 11;       // TEMPERING_SHIFT_U(y) 
    y ^= (y << 7) & TEMPERING_MASK_B;  // TEMPERING_SHIFT_S(y) 
    y ^= (y << 15) & TEMPERING_MASK_C;  // TEMPERING_SHIFT_T(y) 
    y ^= (y >>> 18);      // TEMPERING_SHIFT_L(y) 

    return y >>> (32 - bits); // hope that's right! 
} 

/* If you've got a truly old version of Java, you can omit these 
two next methods. */ 
private synchronized void writeObject(final ObjectOutputStream out) 
     throws IOException { 
    // just so we're synchronized. 
    out.defaultWriteObject(); 
} 

private synchronized void readObject(final ObjectInputStream in) 
     throws IOException, ClassNotFoundException { 
    // just so we're synchronized. 
    in.defaultReadObject(); 
} 

/** This method is missing from jdk 1.0.x and below. JDK 1.1 
includes this for us, but what the heck.*/ 
public boolean nextBoolean() { 
    return next(1) != 0; 
} 

/** This generates a coin flip with a probability <tt>probability</tt> 
of returning true, else returning false. <tt>probability</tt> must 
be between 0.0 and 1.0, inclusive. Not as precise a random real 
event as nextBoolean(double), but twice as fast. To explicitly 
use this, remember you may need to cast to float first. */ 
public boolean nextBoolean(final float probability) { 
    if (probability < 0.0f || probability > 1.0f) { 
     throw new IllegalArgumentException("probability must be between 0.0" 
       + " and 1.0 inclusive."); 
    } 
    if (probability == 0.0f) { 
     return false;   // fix half-open issues 
    } else if (probability == 1.0f) { 
     return true;  // fix half-open issues 
    } 
    return nextFloat() < probability; 
} 

/** This generates a coin flip with a probability <tt>probability</tt> 
of returning true, else returning false. <tt>probability</tt> must 
be between 0.0 and 1.0, inclusive. */ 
public boolean nextBoolean(final double probability) { 
    if (probability < 0.0 || probability > 1.0) { 
     throw new IllegalArgumentException("probability must be between 0.0" 
       + " and 1.0 inclusive."); 
    } 
    if (probability == 0.0) { 
     return false;    // fix half-open issues 
    } else if (probability == 1.0) { 
     return true; // fix half-open issues 
    } 
    return nextDouble() < probability; 
} 

/** This method is missing from JDK 1.1 and below. JDK 1.2 
includes this for us, but what the heck. */ 
public int nextInt(final int n) { 
    if (n <= 0) { 
     throw new IllegalArgumentException("n must be >= 0"); 
    } 

    if ((n & -n) == n) { 
     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; 
} 

/** This method is for completness' sake. 
Returns a long drawn uniformly from 0 to n-1. Suffice it to say, 
n must be > 0, or an IllegalArgumentException is raised. */ 
public long nextLong(final long n) { 
    if (n <= 0) { 
     throw new IllegalArgumentException("n must be >= 0"); 
    } 

    long bits, val; 

    do { 
     bits = (nextLong() >>> 1); 
     val = bits % n; 
    } while (bits - val + (n - 1) < 0); 
    return val; 
} 

/** A bug fix for versions of JDK 1.1 and below. JDK 1.2 fixes 
this for us, but what the heck. */ 
public double nextDouble() { 
    return (((long) next(26) << 27) + next(27)) 
      /(double) (1L << 53); 
} 

/** A bug fix for versions of JDK 1.1 and below. JDK 1.2 fixes 
this for us, but what the heck. */ 
public float nextFloat() { 
    return next(24)/((float) (1 << 24)); 
} 

/** A bug fix for all versions of the JDK. The JDK appears to 
use all four bytes in an integer as independent byte values! 
Totally wrong. I've submitted a bug report. */ 
public void nextBytes(final byte[] bytes) { 
    for (int x = 0; x < bytes.length; x++) { 
     bytes[x] = (byte) next(8); 
    } 
} 

/** For completeness' sake, though it's not in java.util.Random. */ 
public char nextChar() { 
    // chars are 16-bit UniCode values 
    return (char) (next(16)); 
} 

/** For completeness' sake, though it's not in java.util.Random. */ 
public short nextShort() { 
    return (short) (next(16)); 
} 

/** For completeness' sake, though it's not in java.util.Random. */ 
public byte nextByte() { 
    return (byte) (next(8)); 
} 

/** A bug fix for all JDK code including 1.2. nextGaussian can theoretical 
* ly 
ask for the log of 0 and divide it by 0! See Java bug 
<a href="http://developer.java.sun.com/developer/bugParade/bugs/4254501.h 
* tml"> 
http://developer.java.sun.com/developer/bugParade/bugs/4254501.html</a> 
*/ 
synchronized public double nextGaussian() { 
    if (__haveNextNextGaussian) { 
     __haveNextNextGaussian = false; 
     return __nextNextGaussian; 
    } else { 
     double v1, v2, s; 

     do { 
      v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0 
      v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0 
      s = v1 * v1 + v2 * v2; 
     } while (s >= 1 || s == 0); 
     double multiplier = /* Strict*/ Math.sqrt(-2 
       * /* Strict*/ Math.log(s)/s); 

     __nextNextGaussian = v2 * multiplier; 
     __haveNextNextGaussian = true; 
     return v1 * multiplier; 
    } 
} 
} 
2

Bạn không cần phải lo lắng về việc khởi đầu lại vv nếu bạn sử dụng một Set (mà đảm bảo giá trị duy nhất):

Random generator = new Random(); 
Set<Double> numbers = new HashSet<Double>(); 
while (numbers.size() < n) 
    numbers.add(generator.nextDouble()); 

Mặc dù những gì bạn có thể nghĩ rằng, điều này thực hiện khá nhanh chóng: 60 mili giây cho 100000 số trên PC (điển hình) của tôi.

Nếu bạn thực sự muốn một mảng, bạn có thể trích xuất nó từ tập hợp.
Nếu bạn muốn duy trì thứ tự được tạo, hãy sử dụng LinkedHashSet (nó có hiệu suất tương tự)

+0

Hmm. Vì vậy, thực tế chỉ dừng lại này nói rằng chu kỳ lớn hơn 10^5, phải không? – Uri

+0

@Bohemian Làm cách nào để trả lời câu hỏi cho thời lượng của máy phát điện? –

+0

@ChristianSemrau Bởi vì, nếu bạn đọc câu hỏi, những gì anh ta thực sự yêu cầu là 10^5 * khác nhau * tăng gấp đôi. Câu trả lời là "không anh ấy không cần phải gieo hạt" nếu anh ta sử dụng impl này. – Bohemian

Các vấn đề liên quan