【问题标题】:How many times can I use randomGenerator.nextDouble() before I need to reseed?在需要重新播种之前,我可以使用多少次 randomGenerator.nextDouble()?
【发布时间】:2012-04-18 19:27:51
【问题描述】:

我在 Java 中使用 Random 类作为伪随机数生成器。我多次使用函数 nextDouble (~10^5)。在我必须重新播种之前多少次才能防止获得相同的数字?需要补种吗?

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

这是一个实验,数字将用作空间上点的坐标,所以我希望分布尽可能均匀。

另外,我该如何重新播种?我从哪里获得 int 种子?

【问题讨论】:

标签: java random


【解决方案1】:

随机数生成器将从两个随机 int 值中生成一个随机双精度值。内部种子有 48 位,因此随机序列在最多 2^48 个 int 值或 2^47 个 double 值之后重复。

【讨论】:

    【解决方案2】:

    如果您使用 Set(保证唯一值),则无需担心重新播种等问题:

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

    不管你怎么想,这执行得非常快:在我的(典型)PC 上,100000 个数字需要 60 毫秒。

    如果你真的想要一个数组,你可以从集合中提取它。
    如果您想保持它们的生成顺序,请使用LinkedHashSet(它具有相似的性能)

    【讨论】:

    • 嗯。所以这个停止的事实表明周期大于 10^5,对吧?
    • @Bohemian 如何回答生成器周期长度的问题?
    • @ChristianSemrau 因为,如果您阅读了这个问题,他实际要求的是 10^5 不同 双打。如果他使用这个 impl,答案是“不,他不需要重新播种”。
    • @Bohemian 好吧,如果生成器不产生重复项,那么使用数组的原始 impl 也一样好。 OTOH,如果生成器产生重复,它将继续这样做,因为循环已经耗尽了生成器的周期长度,并且您的循环不会结束。为了在不停止程序的情况下生成唯一值,可以检查 numbers.add 的返回值以查看是否找到重复项。
    • @ChristianSemrau 好点。如果n 太大,循环将永远不会结束,但是在您用完数字之前,您将耗尽内存来保存 Set long - 即不可能有足够的内存可以容纳 2^48 个数字……这需要 262144 Gb!我会说使用我的实现几年是安全的
    【解决方案3】:

    很抱歉,我无法直接回答您的问题。我不记得 Java 的随机数生成器的循环时间。尽管我确实认为您正在将其与您生成的数字数量相提并论。

    但是,我在计算机工程统计课程中学到的知识可能会对您有所帮助。

    我了解到生成最多随机数的最佳方法是使用 Mersenne Twister 随机数生成器。此生成器将为您提供足够的随机数,无需重新设置种子,它的周期为 (2^19937) - 1

    这是 MerseneTwister 的源代码

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

    这是一个生成随机数的类。

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

    这是一个生成随机数的示例。请注意,我从源代码中删除了 cmets。这可能会破坏代码的开源性质,但我无法将其全部复制并格式化为代码。

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

    【讨论】:

      猜你喜欢
      • 2020-02-01
      • 2013-02-17
      • 2016-01-06
      • 1970-01-01
      • 2016-07-15
      • 2011-10-10
      • 2015-04-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多