objective-c - repetir - numeros aleatorios que no se repitan
Números aleatorios no repetitivos (6)
Además de usar matriz secundaria para almacenar números aleatorios ya generados, invocando no aleatorios. función de siembra antes de cada llamada de no aleatorio la función de generación puede ayudar a generar diferentes seq. de números aleatorios en cada carrera.
Estoy usando
for (int i = 1, i<100, i++)
int i = arc4random() % array count;
pero me repiten todo el tiempo. ¿Cómo puedo completar el valor int
elegido del rango, de modo que cuando el programa se repita, no obtenga ningún duplicado?
Debe realizar un seguimiento de los números que ya ha utilizado (por ejemplo, en una matriz). Obtenga un número aleatorio y deséchelo si ya se utilizó.
La mejor manera de hacerlo es crear una matriz para números ya utilizados. Después de que se haya creado un número aleatorio, agréguelo al conjunto. Luego, cuando vaya a crear otro número aleatorio, asegúrese de que no esté en la matriz de números usados.
Sin depender de procesos estocásticos externos, como la descomposición radiactiva o la entrada del usuario, las computadoras siempre generarán números pseudoaleatorios, es decir, números que tienen muchas de las propiedades estadísticas de números aleatorios, pero que se repiten en secuencias.
Esto explica las sugerencias para aleatorizar la salida de la computadora barajando.
El descarte de los números previamente utilizados puede alargar la secuencia artificialmente, pero a un costo para las estadísticas que dan la impresión de aleatoriedad.
Parece que quieres barajar un conjunto en lugar de aleatoriedad "verdadera". Simplemente crea una matriz donde todas las posiciones coinciden con los números e inicializa un contador:
num[ 0] = 0
num[ 1] = 1
: :
num[99] = 99
numNums = 100
Luego, cuando quiera un número aleatorio, use el siguiente método:
idx = rnd (numNums); // return value 0 through numNums-1
val = num[idx]; // get then number at that position.
num[idx] = val[numNums-1]; // remove it from pool by overwriting with highest
numNums--; // and removing the highest position from pool.
return val; // give it back to caller.
Esto devolverá un valor aleatorio de un grupo cada vez menor, lo que garantiza que no se repitan. Deberá tener cuidado con el grupo que se está ejecutando hasta el tamaño cero, por supuesto, e inteligentemente reiniciar el grupo.
Esta es una solución más determinística que mantener una lista de números usados y continuar en bucle hasta que encuentre uno que no esté en esa lista. El rendimiento de ese tipo de algoritmo se degradará a medida que el grupo se haga más pequeño.
La función de CA usando valores estáticos como este debería hacer el truco. Llámalo con
int i = myRandom (200);
para configurar el grupo (con cualquier número cero o superior que especifique el tamaño) o
int i = myRandom (-1);
para obtener el siguiente número del grupo (cualquier número negativo será suficiente). Si la función no puede asignar suficiente memoria, devolverá -2. Si no quedan números en el grupo, devolverá -1 (en ese punto podría reiniciar el grupo si lo desea). Aquí está la función con una prueba de unidad principal para que la pruebes:
#include <stdio.h>
#include <stdlib.h>
#define ERR_NO_NUM -1
#define ERR_NO_MEM -2
int myRandom (int size) {
int i, n;
static int numNums = 0;
static int *numArr = NULL;
// Initialize with a specific size.
if (size >= 0) {
if (numArr != NULL)
free (numArr);
if ((numArr = malloc (sizeof(int) * size)) == NULL)
return ERR_NO_MEM;
for (i = 0; i < size; i++)
numArr[i] = i;
numNums = size;
}
// Error if no numbers left in pool.
if (numNums == 0)
return ERR_NO_NUM;
// Get random number from pool and remove it (rnd in this
// case returns a number between 0 and numNums-1 inclusive).
n = rand() % numNums;
i = numArr[n];
numArr[n] = numArr[numNums-1];
numNums--;
if (numNums == 0) {
free (numArr);
numArr = 0;
}
return i;
}
int main (void) {
int i;
srand (time (NULL));
i = myRandom (20);
while (i >= 0) {
printf ("Number = %3d/n", i);
i = myRandom (-1);
}
printf ("Final = %3d/n", i);
return 0;
}
Y aquí está el resultado de una ejecución:
Number = 19
Number = 10
Number = 2
Number = 15
Number = 0
Number = 6
Number = 1
Number = 3
Number = 17
Number = 14
Number = 12
Number = 18
Number = 4
Number = 9
Number = 7
Number = 8
Number = 16
Number = 5
Number = 11
Number = 13
Final = -1
Tenga en cuenta que, debido a que usa estadísticas, no es seguro llamar desde dos lugares diferentes si desean mantener sus propios grupos separados. Si ese fuera el caso, la estática se reemplazaría con un búfer (conteo de espera y reserva) que "pertenecería" a la persona que llama (se podría pasar un doble puntero para este fin).
Y, si está buscando la versión de "múltiples grupos", la incluyo aquí para que esté completa.
#include <stdio.h>
#include <stdlib.h>
#define ERR_NO_NUM -1
#define ERR_NO_MEM -2
int myRandom (int size, int *ppPool[]) {
int i, n;
// Initialize with a specific size.
if (size >= 0) {
if (*ppPool != NULL)
free (*ppPool);
if ((*ppPool = malloc (sizeof(int) * (size + 1))) == NULL)
return ERR_NO_MEM;
(*ppPool)[0] = size;
for (i = 0; i < size; i++) {
(*ppPool)[i+1] = i;
}
}
// Error if no numbers left in pool.
if (*ppPool == NULL)
return ERR_NO_NUM;
// Get random number from pool and remove it (rnd in this
// case returns a number between 0 and numNums-1 inclusive).
n = rand() % (*ppPool)[0];
i = (*ppPool)[n+1];
(*ppPool)[n+1] = (*ppPool)[(*ppPool)[0]];
(*ppPool)[0]--;
if ((*ppPool)[0] == 0) {
free (*ppPool);
*ppPool = NULL;
}
return i;
}
int main (void) {
int i;
int *pPool;
srand (time (NULL));
pPool = NULL;
i = myRandom (20, &pPool);
while (i >= 0) {
printf ("Number = %3d/n", i);
i = myRandom (-1, &pPool);
}
printf ("Final = %3d/n", i);
return 0;
}
Como puede ver desde la main()
modificada main()
, primero necesita inicializar un puntero int
a NULL
luego pasar su dirección a la función myRandom()
. Esto permite que cada cliente (ubicación en el código) tenga su propio grupo que se asigna y libera automáticamente, aunque aún así puede compartir grupos si lo desea.
Puede usar el Cifrado de Preservación de Formato para encriptar un contador. Su contador simplemente va de 0 hacia arriba, y el cifrado utiliza una clave de su elección para convertirlo en un valor aparentemente aleatorio de cualquier raíz y ancho que desee.
Los cifrados de bloque normalmente tienen un tamaño de bloque fijo de, por ejemplo, 64 o 128 bits. Pero el Cifrado de Preservación de Formato le permite tomar un cifrado estándar como AES y crear un cifrado de ancho menor, de cualquier radix y ancho que desee (p. Ej., Radix 2, ancho 16), con un algoritmo criptográficamente robusto.
Se garantiza que nunca tendrá colisiones (porque los algoritmos criptográficos crean una asignación de 1: 1). También es reversible (un mapeo bidireccional), por lo que puede tomar el número resultante y volver al valor del contador con el que comenzó.
AES-FFX es un método estándar propuesto para lograr esto. He experimentado con un código básico de Python que se basa en la idea de AES-FFX, aunque no totalmente conforme. Consulte aquí el código de Python . Por ejemplo, puede encriptar un contador a un número decimal de 7 dígitos de aspecto aleatorio o un número de 16 bits.