这是示例代码
int x = rand() % 90000;
当我做这样的事情时,我意识到所有数字都在0-30000左右。 这有限制吗?如果有我该如何无限制地使用它?
答案 0 :(得分:0)
您不应使用rand()
,因为它在许多C标准库实现中都非常差。它将返回一个介于0和RAND_MAX
之间的伪随机数,包括0和RAND_MAX
,但是RAND_MAX
通常相对较小。例如32767。
如果范围是生成器函数可以返回的值范围的很大一部分,则使用模运算符来产生整数范围是有问题的,因为分布并不完全均匀。
例如,假设rand() % 40000
是59999,而我们使用了rand()
。结果介于0和19999之间的概率为67%,但介于20000和39999之间的概率仅为33%。这是因为rand()
在[0,19999]中以1/3的概率[20000, 39999]的概率为1/3,[40000..59999]的概率为1/3;但是最后三分之一折回去,以便在取模后得到[0,19999]!
在小范围内,偏差不太明显。
我个人希望生成足够的随机位来覆盖所需的范围,然后使用排除方法来选择值。
如果需要使用atleast
,则可以使用以下帮助函数来生成伪随机数,其范围至少为#include <inttypes.h>
static inline uint64_t rand_atleast(uint64_t atleast)
{
uint64_t result = 0;
do {
result = ((uint64_t)RAND_MAX + 1) * result + (uint64_t)rand();
atleast /= ((uint64_t)RAND_MAX + 1);
} while (atleast > 0);
return result;
}
(但可以更大;即,它可以返回更大的值) :
int
要使用排除方法在所需范围内创建struct range_spec {
uint64_t mask;
uint64_t limit;
int base;
};
static inline void set_range(struct range_spec *spec,
int minimum, int maximum)
{
uint64_t mask;
int base;
if (minimum <= maximum) {
base = minimum;
mask = maximum - minimum;
} else {
base = maximum;
mask = minimum - maximum;
}
spec->base = base;
spec->limit = mask;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
mask |= mask >> 32;
spec->mask = mask;
}
static inline int rand_range(const struct range_spec *spec)
{
const uint64_t mask = spec->mask;
const uint64_t limit = spec->limit;
uint64_t result;
do {
result = rand_atleast(mask) & mask;
} while (result > limit);
return spec->base + result;
}
,我们可以使用一种结构来包含所需的内容,可以使用一个辅助函数来初始化该范围(以描述某个特定的int范围),另一个辅助函数来生成该范围内的整数:
#ifndef RNG64_H
#define RNG64_H
#include <inttypes.h>
#include <time.h>
typedef struct {
uint64_t limit;
int64_t base;
int shift;
} rng64_intrange_spec;
static uint64_t rng64_state = 1;
static inline uint64_t rng64(void)
{
uint64_t x = rng64_state;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
rng64_state = x;
return x * UINT64_C(2685821657736338717);
}
static inline uint64_t rng64_randomize(void)
{
uint64_t x;
int n = 1000;
x = ((uint64_t)time(NULL) * UINT64_C(19076794157513))
^ ((uint64_t)clock() * UINT64_C(809712647));
if (!x)
x = 1;
while (n-->0) {
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
}
rng64_state = x;
return x;
}
static inline double rng64_one(void)
{
return (double)rng64() / 18446744073709551616.0;
}
static inline int64_t rng64_intrange(rng64_intrange_spec *spec)
{
const uint64_t limit = spec->limit;
const int shift = spec->shift;
uint64_t value;
do {
value = rng64() >> shift;
} while (value > limit);
return spec->base + value;
}
static inline void rng64_set_intrange(rng64_intrange_spec *spec,
int64_t minimum,
int64_t maximum)
{
int64_t base;
uint64_t limit;
int bits = 0;
if (minimum <= maximum) {
base = minimum;
limit = maximum - minimum;
} else {
base = maximum;
limit = minimum - maximum;
}
spec->base = base;
spec->limit = limit;
while (limit >= 32768) {
limit >>= 16;
bits += 16;
}
while (limit >= 8) {
limit >>= 4;
bits += 4;
}
while (limit > 0) {
limit >>= 1;
bits += 1;
}
spec->shift = 64 - bits;
}
#endif /* RNG64_H */
但是,要获得相当差的伪随机数,需要做很多工作:我认为这不值得。
我通常改用Xorshift64 *。它速度快,相当随机(请参阅我的this extended comment),并且非常容易实现。
基本上,您可以使用一个较小的头文件,例如 rng64.h :
rng64_randomize()
在程序开始附近的某个地方,调用time()
以根据当前时间(通过clock()
的挂钟以及用于通过{{1}执行当前进程的CPU时间)生成状态。 }。初始状态会有所调整,以确保快速连续运行代码时不会出现类似的序列。您可以将rng64_state
设置为除零以外的任何值,以生成特定的序列。 (零状态将仅生成零。)我建议使用
printf("Using %" PRIu64 " as the Xorshift64* random number seed.\n", rng64_randomize());
会在程序开始附近打印种子和所使用的伪随机数生成器算法。这允许某人重现测试(通过将rng64_state
设置为该值而不是调用rng64_randomize()
,或使用他们自己的等效代码重新实现测试)。重现性好。
虽然(uint64_t)time(NULL)
不能保证按C标准运行,但确实可以在我所知道的所有当前广泛使用的C实现中使用。
如果要与其他伪随机数生成器进行比较,只需使用类似的头文件重新实现另一个,然后包括它即可。这样,您无需更改任何使用生成器的代码,只需更改生成器代码本身即可。
rng_one()
返回0到1.0(含)之间的统一伪随机数。如果您希望上限是互斥的,请使用例如
static inline double rng64_one(void)
{
double r;
do {
r = (double)rng64() / 18446744073709551616.0;
} while (r >= 1.0);
return r;
}
,并且如果两个限制都互斥(因此它永远不会精确返回0.0或1.0),则改为while (r <= 0.0 || r >= 1.0);
。
这是上面的rng64.h的使用示例:
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include "rng64.h"
int main(int argc, char *argv[])
{
rng64_intrange_spec r;
int minval, maxval, count, i;
char dummy;
if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s MIN MAX COUNT\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program outputs COUNT pseudorandom integers,\n");
fprintf(stderr, "between MIN and MAX, inclusive.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (sscanf(argv[1], " %d %c", &minval, &dummy) != 1) {
fprintf(stderr, "%s: Invalid minimum.\n", argv[1]);
return EXIT_FAILURE;
}
if (sscanf(argv[2], " %d %c", &maxval, &dummy) != 1 || maxval < minval) {
fprintf(stderr, "%s: Invalid maximum.\n", argv[2]);
return EXIT_FAILURE;
}
if (sscanf(argv[3], " %d %c", &count, &dummy) != 1 || count < 0) {
fprintf(stderr, "%s: Invalid count.\n", argv[3]);
return EXIT_FAILURE;
}
fprintf(stderr, "Generating %d pseudorandom integers in [%d, %d],\n", count, minval, maxval);
fprintf(stderr, "using Xorshift64* with seed %" PRIu64 ".\n", rng64_randomize());
fflush(stderr);
rng64_set_intrange(&r, minval, maxval);
for (i = 0; i < count; i++)
printf("%d\n", (int)rng64_intrange(&r));
return EXIT_SUCCESS;
}
指定最小值和最大值(整数)以及要输出的整数数作为命令行参数。