我正在使用
for (int i = 1, i<100, i++)
int i = arc4random() % array count;
但我每次都会重复。如何从范围中填写所选的int
值,以便在程序循环时我不会得到任何欺骗?
答案 0 :(得分:16)
听起来你想要改变一套而不是“真正的”随机性。只需创建一个数组,其中所有位置都与数字匹配并初始化计数器:
num[ 0] = 0
num[ 1] = 1
: :
num[99] = 99
numNums = 100
然后,只要您想要一个随机数,请使用以下方法:
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.
这将从不断减少的池中返回一个随机值,保证不重复。当然,您必须注意池的运行量为零,并智能地重新初始化池。
这是一个更确定的解决方案,而不是保留已使用数字的列表并继续循环,直到找到不在该列表中的数字。随着池变小,这种算法的性能会降低。
使用类似这样的静态值的C函数应该可以解决问题。用
调用它int i = myRandom (200);
设置池(任何数字为零或更大,指定大小)或
int i = myRandom (-1);
从池中获取下一个数字(任何负数就足够了)。如果函数无法分配足够的内存,则返回-2。如果池中没有剩余数字,它将返回-1(此时您可以根据需要重新初始化池)。这是一个单元测试主要功能供您试用:
#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;
}
这是一次运行的输出:
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
请记住,因为它使用静态,如果他们想要维护自己独立的池,则从两个不同的地方打电话是不安全的。如果是这种情况,静态将被替换为“属于”调用者的缓冲区(保持计数和池)(为此目的可以传入双指针)。
而且,如果您正在寻找“多池”版本,我会将其包含在此处以便完整。
#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;
}
从修改后的main()
可以看出,您需要先将int
指针初始化为NULL
,然后将其地址传递给myRandom()
函数。这允许每个客户端(代码中的位置)拥有自己的池,并自动分配和释放,但如果您愿意,仍然可以共享池。
答案 1 :(得分:1)
您可以使用Format-Preserving Encryption加密计数器。你的计数器从0开始向上,加密使用你选择的一个键将它变成一个看似随机的值,无论你想要什么基数和宽度。
分组密码通常具有固定的块大小,例如, 64或128位。但是格式保留加密允许您采用AES之类的标准密码,并使用您想要的任何基数和宽度(例如基数2,宽度16)制作更小宽度的密码,并且算法仍然具有加密功能。
保证永远不会发生冲突(因为加密算法会创建1:1映射)。它也是可逆的(双向映射),因此您可以获取结果数字并返回到您开始时的计数器值。
AES-FFX是一种提出标准的方法来实现这一目标。我已经尝试了一些基于AES-FFX理念的基本Python代码,虽然不完全符合 - see Python code here。它可以是例如将计数器加密为随机查看的7位十进制数或16位数。
答案 2 :(得分:0)
您需要跟踪已使用的数字(例如,在数组中)。获取一个随机数,如果已经使用过,则将其丢弃。
答案 3 :(得分:0)
如果不依赖外部随机过程,如放射性衰变或用户输入,计算机将始终生成伪随机数 - 即具有随机数的许多统计属性但在序列中重复的数字。
这解释了通过改组随机计算机输出的建议。
丢弃以前使用过的数字可能会人为地延长序列,但会给出给出随机性印象的统计数据。
答案 4 :(得分:0)
执行此操作的最佳方法是为已使用的数字创建一个数组。创建随机数后,将其添加到数组中。然后,当您创建另一个随机数时,请确保它不在已使用数字的数组中。
答案 5 :(得分:0)
除了使用辅助数组存储已生成的随机数,还要调用随机数。播种功能在每次随机播放之前都没有。生成函数可能有助于生成不同的seq。每次运行中的随机数。