我想使用std::rand()
生成0
和amountOfNumbers
之间的数字,但生成的数字不能与上次生成的数字相同。
我写了这个函数:
void reroll() {
int newRand;
while (true) {
newRand = std::rand() % (amountOfNumbers);
if (newRand == lastNumber) {
continue;
}
lastNumber = newRand;
break;
}
// do something with newRand
// ...
}
amountOfNumbers
只是一个int(> 1),它定义了上限(例如5,因此可能的数字范围是0到4)。 lastNumber
最初-1
存储最后生成的数字。
我想知道是否有更好的方式来写这个。
到目前为止,该功能似乎正在运行,但我不确定我的代码是否完美......它看起来有点糟糕。那while (true)
让我感到有点不安。
答案 0 :(得分:3)
代码可以工作,但我会像这样构建它
int reroll(int n, int last) {
while(true) {
int v = std::rand() % n;
if(v!=last)
return v;
}
}
void reroll() {
...
int v = reroll(n, last);
...
}
此外,您可以通过生成较小范围内的值(少1个)并调整last
来完全避免使用while循环。
int reroll(int n, int last) {
if(last==-1)
return std::rand() % n;
int v = std::rand() % (n-1);
if (v<last) return v
return v+1;
}
答案 1 :(得分:2)
我有一些建议。
由于你从未声明lastNumber和amountOfNumbers,我将假设它们是全局的。最好将这些变量作为变量传递给函数。此外,您应该从函数返回新数字,而不是将其设置为void。
以下代码将计算新的代码。我们不会重新滚动,直到有新的滚动,我们将只取一组数字的随机数,但少一个。如果数字大于或等于,我们将重新添加一个,从而避免使用lastNumber。然后该函数返回newRand。这样做可以避免无限循环(尽管很低)的风险,并且它总是在恒定时间内运行。
int reroll(int lastNumber, int amountOfNumbers)
{
int newRand;
newRand = std::rand() % (amountOfNumbers - 1);
if (newRand >= lastNumber) {
newRand++;
}
return newRand;
}
答案 2 :(得分:0)
while true循环绝对不是一个好习惯,我建议做这样的事情。但是你应该像迈克尔的答案一样使用它,就像这样:
void reroll(int lastNumber, int amountOfNumbers) {
int newRand = std::rand() % (amountOfNumbers);
while (newRand == lastNumber) {
newRand = std::rand() % (amountOfNumbers);
}
lastNumber = newRand;
return newRand;
}
答案 3 :(得分:0)
根据您amountOfNumbers
的值,您使用的模数运算可能无法保证均匀分布(这只是您问题的一小部分)。
amountOfNumbers
大于RAND_MAX
,那么您将永远不会看到使用此数字的数字。[0,1,2,3,4,5]
),RAND_MAX
为7.您将看到值0
和1
两倍的频率!实际上,RAND_MAX
必须至少为32767(无论这意味着什么,根据标准不是很多)......但 不能被6整除,所以当然会有一些具有轻微偏见的值。你可以使用模数来缩小范围,但你可能想要在第二个问题中放弃偏差。不幸的是,由于rand
的共同实施,将范围扩展到max
以后会引入进一步的偏见。
unsigned int rand_range(unsigned int max) {
double v = max > RAND_MAX ? max : RAND_MAX;
v /= RAND_MAX;
int n;
do {
n = rand();
} while (RAND_MAX - n <= RAND_MAX % (unsigned int)(max / v));
return (unsigned int)(v * n) % max;
}
此仍并不保证不存在任何重复值,但至少会导致重复值的任何偏差显着减少。我们可以使用类似的方法(当前)接受的答案来删除任何重复的值,现在具有最小的偏差:
unsigned int non_repetitive_rand_range(unsigned int max) {
if (max <= 1) {
return 0; // Can't avoid repetitive values here!
}
static unsigned int previous;
unsigned int current;
do {
current = rand_range(max);
} while (current != previous);
previous = current;
return current;
}
从技术角度来看,这并不一定能保证解决方案的问题。该标准的引用解释了原因:
不能保证产生的随机序列的质量,并且已知一些实现产生具有令人沮丧的非随机低阶位的序列。具有特殊要求的应用应使用已知足以满足其需求的发电机。
因此,某些愚蠢,模糊的实现可能会像{I}那样实现rand
:
int rand(void) {
return 0;
}
对于这样的实现,没有避免它:rand
的这种实现会导致此答案中的代码(以及所有其他当前答案)进入无限循环。您唯一的希望是重新实施rand
和srand
。这是标准中给出的示例实现,如果您必须这样做:
static unsigned long int next = 1;
int rand(void) // RAND_MAX assumed to be 32767
{
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}
void srand(unsigned int seed)
{
next = seed;
}
您可能希望将它们分别重命名为my_rand
和my_srand
,并使用#define rand my_rand
和#define srand my_srand
或使用查找/替换操作。