Algoritmo de reproducción aleatoria de longitud fija

1. Antecedentes

Durante la prueba escrita, encontré un problema algorítmico: es casi un número aleatorio de m que no se repiten a partir de n números diferentes. El algoritmo de barajado consiste en dividir la matriz original para que un cierto número de la matriz original aparezca en cada posición de la matriz rota con la misma probabilidad, lo que simplemente puede resolver el problema.

2. Algoritmo de reproducción aleatoria

Tres algoritmos de barajado se derivan del robo de cartas, el intercambio de cartas y la inserción de cartas, entre los cuales el robo y el intercambio de cartas corresponden a los algoritmos Fisher-Yates Shuffle y Knuth-Durstenfeld Shhuffle respectivamente.

2.1 Fisher-Yates Shuffle

Ronald A. Fisher y Frank Yates, a saber, Fisher-Yates Shuffle, fueron los primeros en proponer este método de mezcla. La idea básica es seleccionar aleatoriamente un número que no se haya tomado antes de la matriz original a la nueva matriz, de la siguiente manera:

  1. Inicialice la matriz original y la nueva matriz, la longitud de la matriz original es n (conocida);
  2. A partir de la matriz sin procesar (asumiendo que quedan k), genera aleatoriamente un elemento p de [0, k) (asumiendo que los elementos están numerados comenzando desde 0)
  3. Tome el p-ésimo número de los k números restantes
  4. Repita 2 y 3 hasta que se agote el número
  5. La secuencia tomada del paso 3 es una secuencia codificada

Lo siguiente demuestra su aleatoriedad, es decir, la i-ésima posición de cada elemento colocado en la nueva matriz es 1 / n (asumiendo que el tamaño de la matriz es n).
Prueba: la probabilidad de que un elemento m se coloque en la i-ésima posición P = la probabilidad de que m no se seleccione cuando el elemento se seleccione en la primera i-1 posición * la probabilidad de que m se seleccione en la i-ésima posición, es decir
Inserte la descripción de la imagen aquí
, si hay un número 5 Secuencia, la probabilidad de que cada número se elimine como el primer número es 1/5, es decir, 1 / n. El
segundo número eliminado se coloca en el segundo de la nueva matriz y cada número se toma como el segundo número. La probabilidad de sacar es 4/5 * 1/4 = 1/5, que es 1 / n

#define N 10
#define M 5
void Fisher_Yates_Shuffle(vector<int>& arr,vector<int>& res)
{
    
    
     srand((unsigned)time(NULL));
     int k;
     for (int i=0;i<M;++i)
     {
    
    
     	
     	k = rand() % arr.size();
     	res.push_back(arr[k]);
     	arr.erase(arr.begin()+k);
     }
}

La complejidad del tiempo es O (n * n) y la complejidad del espacio es O (n).

2.2 Reproducción aleatoria de Knuth-Durstenfeld

Knuth y Durstenfeld mejoraron el algoritmo sobre la base de Fisher et al., Interactuando con los números en la matriz original , ahorrando espacio O (n) adicional. La idea básica del algoritmo es similar a la de Fisher. Cada vez que se toma un número aleatoriamente de los datos no procesados, y luego el número se coloca al final de la matriz, es decir, el número al final de la matriz es el número que se ha procesado.

Los pasos del algoritmo son:

  1. Cree una matriz arr con un tamaño de matriz de n y almacene los valores de 1 an respectivamente;
  2. Genera un número aleatorio x de 0 a n-1; // El último bit también se puede tomar
  3. El subíndice de salida es el valor de x, que es el primer número aleatorio
  4. Intercambia el elemento de cola actual de arr con el elemento xth
  5. Igual que 2, genera un número aleatorio x de 0 a n-2
  6. El subíndice de salida es el valor de x, que es el segundo número aleatorio
  7. Intercambia el penúltimo elemento de arr con el elemento cuyo subíndice es x;

    como arriba, hasta que salgan m números

Este algoritmo es un algoritmo de barajado clásico. Su prueba es la siguiente:
para arr [i], la probabilidad de estar en la n-1a posición después de barajar es 1 / n (el número aleatorio del primer intercambio es i) y
la probabilidad de estar en la posición n-2 es [(n- 1) / n] * [1 / (n-1)] = 1 / n, (el número aleatorio intercambiado por primera vez no es i, y la segunda vez es la ubicación de arr [i] (tenga en cuenta que si i = n -1, el primer arreglo de intercambio [n-1] se cambiará a una posición aleatoria))
La probabilidad de estar en la posición nk es [(n-1) / n] * [(n-2) / (n- 1)] [(n-k + 1) / (n-k + 2)] * [1 / (n-k + 1)] = 1 / n
(el primer número aleatorio no debe ser i, el segundo No es la ubicación de arr [i] (puede cambiar con el intercambio) ... la enésima vez es la ubicación de arr [i]).

void Knuth_Durstenfeld_Shuffle(vector<int>& arr){
    
    
	for(int i = arr.size() - 1; i >= 0; i--){
    
    
		srand(unsigned)time(NULL);
		swap(arr[i], arr[rand() % (i + 1)];
	} 
}

Prueba gráfica
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
La complejidad temporal es O (n) y la complejidad espacial es O (1). Las deficiencias deben conocer la longitud de la matriz n. La
matriz original ha sido modificada. Este es un algoritmo que mezcla el orden en su lugar. La complejidad temporal del algoritmo también proviene del algoritmo de Fisher. El O (n2) se promueve a O (n). Debido a que escanea de atrás hacia adelante, es imposible procesar matrices que no conocen la longitud o crecen dinámicamente.

Supongo que te gusta

Origin blog.csdn.net/J_avaSmallWhite/article/details/109268439
Recomendado
Clasificación