java中的快速实值随机生成器

时间:2015-03-22 10:41:55

标签: java performance random double generator

java.util.Random.nextDouble()对我来说很慢,我需要一些非常快的东西。

我做了一些谷歌搜索,我发现只有基于整数的快速随机生成器。这里是否有来自区间< 0,1)的实数的任何内容?

4 个答案:

答案 0 :(得分:3)

您可以修改基于整数的RNG,以便按以下方式在区间[0,1]中输出双精度数:

double randDouble = randInt()/(RAND_INT_MAX + 1.0)

但是,如果randInt()生成一个32位整数,则不会填充double的所有位,因为double有53个尾数位。您显然可以生成两个随机整数来填充所有尾数位。或者你可以看一下Ramdom.nextDouble()实现的源代码。它几乎肯定使用整数RNG并简单地将输出转换为double。

就性能而言,性能最佳的随机数发生器是线性同余发生器。其中,我建议使用Numerical Recipes生成器。您可以从维基百科中查看有关LCG的更多信息:http://en.wikipedia.org/wiki/Linear_congruential_generator

然而,如果你想要良好的随机性和性能并不那么重要,我认为Mersenne Twister是最好的选择。它还有一个维基百科页面:http://en.wikipedia.org/wiki/Mersenne_Twister

最近有一个名为PCG的随机数生成器,在http://www.pcg-random.org/中有解释。这基本上是LCG的后处理步骤,可以改善LCG输出的随机性。请注意,PCG比LCG慢,因为它只是LCG的后处理步骤。因此,如果性能非常重要且随机性质量不那么重要,那么您希望使用LCG而不是PCG。

请注意,我提到的所有生成器都不具有加密安全性。如果需要使用加密应用程序的值,则应使用加密安全算法。但是,我并不认为双打将用于加密。

答案 1 :(得分:1)

您可以在初始化程序时创建一个随机双打数组,然后重复它。这个速度要快得多,但随机值会使自己重新出现。

答案 2 :(得分:1)

Imho你应该接受juhist的回答 - 这就是原因。

nextDouble很慢,因为它对next()进行了两次调用 - 它在文档中就已经写好了。

所以你最好的选择是:

  • 使用快速64位发生器,将其转换为双倍(MT,PCG,xorshift *,ISAAC64,......)
  • 直接生成双打

这是一个过长的基准测试,包括java的Random,一个LCG(和java.util.Random一样糟糕),以及Marsaglia的通用生成器(生成双打的版本)。

import java.util.*;

public class d01 {
    private static long sec(double x)
    {
        return (long) (x * (1000L*1000*1000));
    }
    // ns/op: nanoseconds to generate a double
    // loop until it takes a second.
    public static double ns_op(Random r)
    {
        long nanos = -1;
        int n;
        for(n = 1; n < 0x12345678; n *= 2) {
            long t0 = System.nanoTime();
            for(int i = 0; i < n; i++)
                r.nextDouble();
            nanos = System.nanoTime() - t0;
            if(nanos >= sec(1))
                break;
            if(nanos < sec(0.1))
                n *= 4;
        }
        return nanos / (double)n;
    }
    public static void bench(Random r)
    {
        System.out.println(ns_op(r) + " " + r.toString());
    }

    public static void main(String[] args)
    {
        for(int i = 0; i < 3; i++) {
            bench(new Random());
            bench(new LCG64(new Random().nextLong()));
            bench(new UNI_double(new Random().nextLong()));
        }
    }
}

// straight from wikipedia
class LCG64 extends java.util.Random {
    private long x;
    public LCG64(long seed) {
        this.x = seed;
    }
    @Override
    public long nextLong() {
        x = x * 6364136223846793005L + 1442695040888963407L;
        return x;
    }
    @Override
    public double nextDouble(){
        return (nextLong() >>> 11) * (1.0/9007199254740992.0);
    }
    @Override
    protected int next(int nbits)
    {
        throw new RuntimeException("TODO");
    }
}


class UNI_double extends java.util.Random {
    // Marsaglia's UNIversal random generator extended to double precision
    // G. Marsaglia, W.W. Tsang / Statistics & Probability Letters 66 (2004) 183 – 187
    private final double[] U = new double[98];
    static final double r=9007199254740881.0/9007199254740992.;
    static final double d=362436069876.0/9007199254740992.0;
    private double c=0.;
    private int i=97,j=33;
    @Override
    public double nextDouble(){
            double x;

            x=U[i]- U[j];
            if(x<0.0)
                x=x+1.0;
            U[i]=x;

            if(--i==0) i=97;
            if(--j==0) j=97;

            c=c-d;
            if(c<0.0)
                c=c+r;

            x=x-c;
            if(x<0.)
                return x+1.;
            return x;
        }
    //A two-seed function for filling the static array U[98] one bit at a time
    private
        void fillU(int seed1, int seed2){
            double s,t;
            int x,y,i,j;
            x=seed1;
            y=seed2;

            for (i=1; i<98; i++){
                s= 0.0;
                t=0.5;

                for (j=1; j<54; j++){
                    x=(6969*x) % 65543;
                    // typo in the paper:
                    //y=(8888*x) % 65579;
                    //used forthe demo in the last page of the paper.
                    y=(8888*y) % 65579;
                    if(((x^y)& 32)>0)
                        s=s+t;
                    t=.5*t;
                }
                if(x == 0)
                    throw new IllegalArgumentException("x");
                if(y == 0)
                    throw new IllegalArgumentException("y");
                U[i]=s;
            }
        }

    // Marsaglia's test code is useless because of a typo in fillU():
    //  x=(6969*x)%65543;
    //  y=(8888*x)% 65579;

    public UNI_double(long seed)
    {
        Random r = new Random(seed);
        for(;;) {
            try {
                fillU(r.nextInt(), r.nextInt());
                break;
            } catch(Exception e) {
                // loop again
            }
        }
    }

    @Override
    protected int next(int nbits)
    {
        throw new RuntimeException("TODO");
    }
}

答案 3 :(得分:-1)

您是否实际进行过基准测试或分析,以证明nextDouble是瓶颈?您是否看过MersenneTwister或L&#39; Ecuyer&#39; streams library等替代品?我建议尝试那些有基准测试的人,但是没有免费的午餐。速度的提高可能会以质量为代价,反之亦然。