¿Cómo podemos generar k enteros aleatorios únicos en el rango [1 … n] con igual probabilidad?

Aquí hay un algoritmo, suponiendo que tiene un método rand (i) para generar un entero aleatorio en [1 … i] con igual probabilidad.

Primero, genere el primer entero en [1 … N] usando rand (N). ¿Cómo podemos seleccionar un segundo entero de [1 … N] con igual probabilidad que no sea igual al entero ya seleccionado? Hay dos maneras de hacer esto.

El primer método es simple pero no determinista. Genere otro entero aleatorio usando rand (N). Si es el mismo que el entero originalmente seleccionado, deséchelo e intente nuevamente. Esto no es determinista, pero se espera que dé una respuesta en no demasiados pasos. También podemos usar este método para encontrar enteros posteriores, pero el número esperado de veces que rand () tendrá que ejecutarse para obtener un número entero que aún no se haya visto seguirá aumentando. Esto sería especialmente un problema cuando K está muy cerca de N. Pero en ese caso, tiene más sentido elegir los números únicos NK para descartar al azar. Como min (K, NK) es como máximo N / 2, nunca tendremos más de N / 2 números para evitar al mirar la salida de rand (). Esto significa que cada vez, se espera que nuestro algoritmo termine en 2 pasos o menos. Como podemos usar una tabla hash para verificar si ya se ha elegido un número entero, el tiempo de ejecución general esperado del algoritmo es O (K). Aquí está el pseudocódigo:

  elegido = {}
 para i = 1 a K:
   hacer:
     x = rand (N)
   mientras x está en elegido

   inserte x en elegido
 fin

Existe un segundo método determinista. Supongamos que ya hemos elegido i enteros. Necesitamos elegir un número entero uniformemente entre los números enteros restantes de Ni. Para esto, genere un entero aleatorio x en el rango [1 … Ni] usando rand (Ni). Ahora podemos encontrar el número x no elegido en [1 … N]. Para esto, mantenga una matriz ordenada de números elegidos, digamos ar. Si el primer elemento de ar es mayor que x, x es el número x no elegido. De lo contrario, incremente x en 1 y mire la posición en ar. Continúe así hasta que la siguiente posición en ar contenga un número mayor que x (o hemos llegado al final de la matriz). Como hemos incrementado x en el número de posiciones sobre las que hemos saltado, hemos encontrado el x número no elegido según lo previsto. Dado que este paso puede llevar tiempo O (i), tiene sentido insertar x en la matriz ordenada utilizando la ordenación por inserción. Por lo tanto, la complejidad general del algoritmo es O (K ^ 2). Aquí está el pseudocódigo

  ar [] = {}
 para i = 0 a K-1
   x = rand (Ni)
   para pos = 0 a i-1:
     si x> ar [pos]:
       descanso
     x + = 1
   fin para

   para j = i a pos + 1:
     ar [j] = ar [j-1]

   ar [pos] = x
 fin

Si N es tan pequeño que una matriz de tamaño N cabe en la memoria, aquí hay un método mucho más fácil, en la línea de Knuth shuffle:

  ar [] = {1 ... N}
 para i = 0 a K-1:
   intercambiar ar [Ni], ar [rand (Ni)]
 fin

Esto es tanto determinista como O (N). Las últimas K posiciones en la matriz tendrán K enteros únicos elegidos de manera uniforme al azar.

Mezcle 1-100 con Fisher-Yates y tome los primeros 90 elementos (por supuesto, solo necesita hacer los primeros 90 pasos de Fisher-Yates)

Suponiendo que tiene una función rand (i) que genera un número aleatorio en [1 … i] con probabilidad uniforme, aquí hay una solución que tiene O (K) complejidad de tiempo y espacio.

Almacene el conjunto inválido de números en una matriz ordenada S [1 … K]. Necesitamos elegir un entero de manera uniforme entre los enteros NK restantes. Para esto, genere un entero aleatorio x en el rango [1 … NK] usando rand (NK). Ahora, si podemos encontrar el número x no elegido en [1 … N], podemos resolver el problema.

Podemos usar la matriz S para encontrar este número. Si el primer elemento de S es mayor que x, x es el número x no elegido. De lo contrario, incremente x en 1 y observe la segunda posición en S. Continúe con esto hasta que la siguiente posición en S contenga un número mayor que x (o hemos llegado al final de la matriz). Como hemos incrementado x en el número de posiciones sobre las que hemos saltado, hemos encontrado el x número no elegido según lo previsto.

  x = rand (NK)
 
 para pos = 1 a K:
  si x 

Dado que hay números NK no elegidos y cada uno de ellos se elige con la misma probabilidad, cada número válido se elige de manera uniforme al azar. Obviamente, este método usa solo memoria O (K). La complejidad del tiempo es O (K) también.

En la calculadora o calculadora, el número aleatorio básico está en el rango de [0,1), denótelo como RAND, luego, para cambiar el rango a [1, n], podemos hacerlo con la transformación como RAND * n +1 (el rango se convierte en [1, n + 1)) y elige el entero del piso, entonces cada número aleatorio que hagamos es igualmente probable en el rango necesario.

Shuffle Fisher-Yates

Genere los n primeros enteros, baraje, tome los k primeros enteros. El problema con este método es que está en O (n) (tanto para la complejidad del espacio como del tiempo), por lo que si n es mucho mayor que k, puede que no sea lo suficientemente rápido o que requiera demasiada memoria. La ventaja es que es muy simple.