2012-04-18 14 views
7

Uso la clase Random en Java como generador de números pseudoaleatorios. Estoy usando la función nextDouble muchas veces (~ 10^5). ¿Cuántas veces antes tengo que resembrar para evitar obtener los mismos números? ¿Es necesario resembrar?¿Cuántas veces puedo usar randomGenerator.nextDouble() antes de tener que resembrar?

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

Esto es para un experimento, los números se utilizan como coordenadas de puntos en un espacio, por lo que quieren que la distribución sea lo más uniforme posible.

Además, ¿cómo puedo resembrar? ¿De dónde saco la semilla int?

+0

¿Has visto http://stackoverflow.com/questions/7291911/? –

+2

En general, un buen generador de números pseudoaleatorios recorrerá todo el conjunto de enteros que representa antes de repetir. Aleatorio parece tener un número entero interno de 48 bits efectivos. –

+0

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

Respuesta

5

El generador de números aleatorios producirá un doble al azar a partir de dos valores enteros aleatorios. La semilla interna tiene 48 bits, por lo que la secuencia aleatoria se repite después de un máximo de 2^48 valores int, o 2^47 valores dobles.

+0

+1: Si usa SecureRandom, no debería necesitar resembrar. –

0

Lo siento, no puedo responder su pregunta directamente. No recuerdo el tiempo de ciclo del generador de números aleatorios de Java. Aunque creo que lo estás reduciendo con la cantidad de números que estás generando.

Pero, lo que aprendí en mis clases de estadística de ingeniería informática podría ser capaz de ayudarte.

Me enteré de que el mejor método para generar la mayoría de los números aleatorios es usar el generador de números aleatorios Mersenne Twister. Este generador le proporcionará suficientes números aleatorios para no necesitar a sembrar de nuevo, tiene un período de (2^19937) - 1

Aquí está el código fuente de MerseeneTwister

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

Aquí está una clase para generar tus números aleatorios.

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

Aquí hay un ejemplo para generar un número aleatorio. Tenga en cuenta que eliminé los comentarios de la fuente. Esto puede alterar la naturaleza de código abierto del código, pero no pude copiarlo todo y tenerlo formateado como código.

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

Usted no necesita preocuparse por la resiembra, etc, si se utiliza un conjunto (que garantiza valores únicos):

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

pesar de lo que se podría pensar, este ejecuta muy rápidamente: 60 milisegundos para 100000 números en una PC (típica).

Si realmente desea una matriz, puede extraerla del conjunto.
Si desea mantener el orden en que se generaron en, use un LinkedHashSet (que tuvo un rendimiento similar)

+0

Hmm. Entonces, el mero hecho de que esto parezca dice que el ciclo es más grande que 10^5, ¿verdad? – Uri

+0

@Bohemian ¿Cómo responde esa pregunta la duración del período del generador? –

+0

@ChristianSemrau Porque, si lees la pregunta, lo que realmente pide es 10^5 * diferentes * dobles. La respuesta es "no, no necesita resembrar" si usa esta impl. – Bohemian

Cuestiones relacionadas