Fast real valued random generator in java

后端 未结 6 1488
借酒劲吻你
借酒劲吻你 2020-12-31 12:51

java.util.Random.nextDouble() is slow for me and I need something really fast.

I did some google search and I\'ve found only integers based fast random generators.

6条回答
  •  长发绾君心
    2020-12-31 13:36

    Imho you should just accept juhist's answer - here's why.

    nextDouble is slow because it makes two calls to next() - it's written right there in the documentation.

    So your best options are:

    • use a fast 64 bit generator, convert that to double (MT, PCG, xorshift*, ISAAC64, ...)
    • generate doubles directly

    Here's an overly long benchmark with java's Random, an LCG (as bad as java.util.Random), and Marsaglia's universal generator (the version generating doubles).

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

提交回复
热议问题