Java Little White pisando el secreto de registro aleatorio

img

¿Vas a escribir el programa de lotería para la reunión anual de la compañía? Los programadores de Java tienen que tener cuidado, hay misterios en la obtención de números aleatorios.

¿Cómo obtener números aleatorios en Java?

Hay muchas formas de obtener números aleatorios. Veamos primero los dos más simples.

private static final Random rnd = new Random();
//第一种
static int random(int n) {
    return n>=0 ? rnd.nextInt(n) : rnd.nextInt(Math.abs(n));
}
//第二种
static int random2(int n) {
    return Math.abs(rnd.nextInt())%n;
}

Veamos cuál es la diferencia entre el primer tipo y el segundo tipo.

¿No es fácil de ver? Ejecutemos un programa simple para ver:

public static void main(String[] args) {
    int n = 1000;
    int di = 0;
    int low = 0;
    for(int i = 0;i < 10_000_000;i++) {
        if(random(n) < Math.abs(n/2)) {
            di++;
        }
        
        if(random2(n) < Math.abs(n/2)) {
            low++;
        }
    }
    System.out.println(di);
    System.out.println(low);
}

Descubrimos que muchos de los números que probamos son iguales, ¿creemos que son similares?

public static void main(String[] args) {        
    int n = 2*(Integer.MAX_VALUE/3);
    int di = 0;
    int low = 0;
    for(int i=0;i<10_000_000;i++) {
        if(random(n)<Math.abs(n/2)) {
            di++;
        }
        
        if(random2(n)<Math.abs(n/2)) {
            low++;
        }
    }
    System.out.println(di);
    System.out.println(low);
}

Ejecute los datos y vea los resultados:

  • Los datos aleatorios son relativamente uniformes;

  • Los datos de random2 casi caen a 2/3 en la primera mitad y solo 1/3 en la segunda mitad.

Llegar al fondo

nextInt () Este método puede verse bien, pero hay tres deficiencias.

La primera desventaja es: si n es una pequeña potencia de 2, después de un período bastante corto, el número aleatorio que genera se repetirá.

La segunda desventaja es que si n no es una potencia de 2, entonces, en promedio, algunos números aparecerán con más frecuencia que otros. En particular, n es relativamente grande, esta deficiencia es muy obvia. Esta es la razón por la cual  2*(Integer.MAX_VALUE/3) hay 2 multiplicaciones.

La tercera desventaja es que, en casos excepcionales, devolverá un número que está fuera del rango especificado o informará un error. Los ejemplos son los siguientes:

public static void main(String[] args) {
    int n=Integer.MIN_VALUE;
    int di=0;
    int low=0;
    for(int i=0;i<10_000_000;i++) {
        if(random(n)<Math.abs(n/2)) {
            di++;
        }
        
        if(random2(n)<Math.abs(n/2)) {
            low++;
        }
    }
    System.out.println(di);
    System.out.println(low);
}

Razón de error Math.abs () devuelve Integer.MIN_VALUE cuando encuentra Integer.MIN_VALUE. Hay un párrafo de este tipo en la descripción del método de abs:

Tenga en cuenta que si el argumento es igual al valor de {@link Integer # MIN_VALUE}, el valor representable más negativo {@code int}, el resultado es el mismo valor, que es negativo.

Nota: en el método Math.abs (), si el valor de entrada es Integer.MIN_VALUE, se devolverá el mismo resultado.

Por otro lado, también puede ver la implementación del código de abs para comprender

public static int abs(int a) { return (a < 0) ? -a : a; }

Supongamos que a = Integer.MIN_VALUE es -2147483648 (0x80000000), suponiendo que el valor de retorno de int 2147483648 se desbordará, porque el valor máximo de int es 2147483647 (0x7fffffff), y después de desbordar se convierte en 0x80000000, que es Integer.MIN_VALUE

El código fuente se describe en detalle de la siguiente manera:

/**
 * Returns the absolute value of an {@code int} value.
 * If the argument is not negative, the argument is returned.
 * If the argument is negative, the negation of the argument is returned.
 *
 * <p>Note that if the argument is equal to the value of
 * {@link Integer#MIN_VALUE}, the most negative representable
 * {@code int} value, the result is that same value, which is
 * negative.
 *
 * @param a the argument whose absolute value is to be determined
 * @return the absolute value of the argument.
 */
 public static int abs(int a) {
 	return (a < 0) ? -a : a;
 }

La biblioteca de clases de Java proporciona un método con semillas para resolver el problema anterior, que es Random.nextInt (n).

Resumen

El generador de números aleatorios implica mucho conocimiento sobre el algoritmo. Afortunadamente, no necesitamos hacer este trabajo nosotros mismos. Podemos usar los resultados ya preparados para nuestro uso, como Random.nextInt (n) o java.security.SecureRandom , O una API de terceros. Nota: Intentamos usar la biblioteca de clases en lugar de desarrollarla nosotros mismos.

Los sistemas Linux tienen / dev / random, / dev / urandom para proporcionar a los usuarios números aleatorios verdaderos.

Se publicaron 1006 artículos originales · elogiados 1891 · 900,000 visitas

Supongo que te gusta

Origin blog.csdn.net/Dream_Weave/article/details/105539630
Recomendado
Clasificación