使用 rand() 获取一个数字,但该数字不能是上次生成的数字
Using rand() to get a number but that number can't be the number that was last generated
我想使用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)。最初为CCD_ 6的CCD_。
我想知道是否有更好的方法来写这篇文章。
到目前为止,这个函数似乎可以工作,但我不确定我的代码是否完美。。。看起来有点糟糕。while (true)
让我感到有点不安。
代码可以工作,但我会像这个一样构建它
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;
}
我有一些建议。
由于您从未声明lastNumber和amountOfNumbers,我假设它们是全局的。最好将这些作为变量传递给函数。此外,您应该从函数中返回新数字,而不是将其设置为void。
下面的代码将计算新的滚动。我们不会重新滚动,直到有一个新的滚动,我们只会随机抽取一组数字,但少一个。如果该数字大于或等于,我们将把该数字加回来,从而避免使用lastNumber。然后函数返回newRand。这样做可以避免无限循环的风险(尽管风险很低),而且它总是在恒定的时间内运行。
int reroll(int lastNumber, int amountOfNumbers)
{
int newRand;
newRand = std::rand() % (amountOfNumbers - 1);
if (newRand >= lastNumber) {
newRand++;
}
return newRand;
}
虽然true循环肯定不是一个好的练习,但我建议这样做。但你应该把它做成和迈克尔上面的答案一样的结构:
void reroll(int lastNumber, int amountOfNumbers) {
int newRand = std::rand() % (amountOfNumbers);
while (newRand == lastNumber) {
newRand = std::rand() % (amountOfNumbers);
}
lastNumber = newRand;
return newRand;
}
根据amountOfNumbers
的值,您使用的模运算可能无法保证均匀分布(这只是问题的一小部分)。
- 首先,如果
amountOfNumbers
大于RAND_MAX
,那么使用它将永远看不到一些数字 - 接下来,考虑一下您是否使用它为骰子生成0到6之间的值(
[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;
}
从技术角度来说,这也不一定能保证解决方案出现问题。标准中的这句话解释了为什么:
对于所产生的随机序列的质量没有保证,并且已知一些实现产生具有令人痛苦的非随机低阶比特的序列。具有特定要求的应用程序应该使用已知足以满足其需求的生成器。
因此,一些愚蠢、晦涩的实现可能会实现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
或使用查找/替换操作。
- 我怎么不能按课程代码的降序对数字进行排序
- 如果静态变量只为程序的整个部分存储了一个副本,为什么我不能使用静态变量交换 2 个数字?
- 为什么 CSpinButtonCtrl 不能正确处理大于 1000 的数字?
- 在 c++ 中不能将超过 10 位数字放入一个变量中
- 为什么我可以在 C++ 中用撇号分隔数字,但不能用 C 分隔数字
- 6位数字不能保证单精度吗?
- C++斐波那契级数不能得到超过 47 个数字
- 逐行计算文件:EOF 重复最后一个数字(不能使用中断)
- "官方"CUDA 减少功能不能接受某些数字?
- 我不能删除双向链表中的最小数字,然后删除下一个
- 为什么这段代码不能正常打印数字?
- N个整数与K对数字有多少个不能相邻的排列?
- 不能算出这个按数字顺序打印数字的c++小程序的最后一个
- 双精度/打印在c++中不能处理14位数字
- 数字不能被4整除的PTR算法
- 我的平方根函数对某些数字不能给出准确的结果
- 为什么我的 64 位计算机不能在 C++ 中存储双精度数字 2^1024?
- 使用 rand() 获取一个数字,但该数字不能是上次生成的数字
- 我的程序应该摆脱不是数字的字符,但不能完全工作,关于我做错了什么有什么想法吗?
- 不能正确比较两个数字