如何从 rand() 获取特定范围的数字?
Posted
技术标签:
【中文标题】如何从 rand() 获取特定范围的数字?【英文标题】:How do I get a specific range of numbers from rand()? 【发布时间】:2010-11-15 05:15:12 【问题描述】:srand(time(null));
printf("%d", rand());
给出一个高范围的随机数(0-32000ish),但我只需要大约 0-63 或 0-127,虽然我不知道如何去做。有什么帮助吗?
【问题讨论】:
这里要记住两件事: 1. 永远不要去掉高位,因为低位通常不太随机。 2. 除非你真的不需要好的随机数,否则不要使用系统提供的 rand()。 以后我会使用更好的RNG,现在我只是在玩弄随机生成图像。 @David:你不需要记住这两件事,只需要记住第二件事。如果您从不使用 rand(),则不必担心其中的哪些位在典型实现中是糟糕的。如果你使用好的 RNG,那么低位和高位一样好。 相关:How to generate a random int in C?。 Here is my answer there,其中包含我的int utils_rand(int min, int max)
func 的定义,它使用rand()
返回一个随机数,该随机数在min
到max
的特定范围内,包括在内,因此也回答了这个问题。我还缩放以允许min
是INT_MIN
和max
是INT_MAX
的情况,这通常不能单独使用rand()
,因为它返回从0
到RAND_MAX
的值,包括(1 /2 那个范围)。
【参考方案1】:
你可以通过在rand函数前面加一个%来改变代码
例如:
rand() % 50
会给你一个 50 范围内的随机数。对你来说,将 50 替换为 63 或 127
【讨论】:
【参考方案2】:此答案不关注随机性,而是关注算术顺序。 要得到一个范围内的数字,通常我们可以这样做:
// the range is between [aMin, aMax]
double f = (double)rand() / RAND_MAX;
double result = aMin + f * (aMax - aMin);
但是,(aMax - aMin) 有可能溢出。例如。 aMax = 1,aMin = -DBL_MAX。更安全的方法是这样写:
// the range is between [aMin, aMax]
double f = (double)rand() / RAND_MAX;
double result = aMin - f * aMin + f * aMax;
基于这个概念,这样的事情可能会导致问题。
rand() % (max_number + 1 - minimum_number) + minimum_number
// 1. max_number + 1 might overflow
// 2. max_number + 1 - min_number might overflow
【讨论】:
【参考方案3】:你可以用这个:
int random(int min, int max)
return min + rand() / (RAND_MAX / (max - min + 1) + 1);
来自:
comp.lang.c 常见问题列表·问题13.16
问:如何获取一定范围内的随机整数?
A:显而易见的方式,
rand() % N /* POOR */
(它试图返回从 0 到 N-1 的数字)很差,因为 许多随机数发生器的低位令人痛苦 非随机。 (见问题13.18。)更好的方法是
(int)((double)rand() / ((double)RAND_MAX + 1) * N)
如果你不想使用浮点,另一种方法是
rand() / (RAND_MAX / N + 1)
如果你只需要以 1/N 的概率做某事,你可以使用
if(rand() < (RAND_MAX+1u) / N)
所有这些方法显然都需要知道 RAND_MAX(ANSI #defines 在
中),并假设 N 远小于 RAND_MAX。当 N 接近 RAND_MAX 时,如果随机数的范围 生成器不是 N 的倍数(即如果 (RAND_MAX+1) % N != 0),所有 这些方法中的一些失败:一些输出发生的频率高于 其他。 (使用浮点没有帮助;问题是 rand 返回 RAND_MAX+1 个不同的值,这些值并不总是均匀的 分成N个桶。)如果这是一个问题,关于唯一的事情 你可以做的是多次调用 rand ,丢弃某些值: unsigned int x = (RAND_MAX + 1u) / N; unsigned int y = x * N; unsigned int r; do r = rand(); while(r >= y); return r / x;
对于这些技术中的任何一种,移动范围都很简单, 如有必要; [M, N] 范围内的数字可以用 像
M + rand() / (RAND_MAX / (N - M + 1) + 1)
(注意,顺便说一下,RAND_MAX 是一个常数,告诉你什么 C 库 rand 函数的固定范围是。你不能设置 RAND_MAX 到其他值,并且没有办法要求 rand 返回其他范围内的数字。)
如果您从返回的随机数生成器开始 介于 0 和 1 之间的浮点值(例如 PMrand 提到有问题的 13.15,或有问题的 drand48 13.21),要获得从 0 到 N-1 的整数,您所要做的就是 将该生成器的输出乘以 N:
(int)(drand48() * N)
Additional links
参考:K&R2 Sec. 7.8.7 页。 168 个人电脑秒。 11 页。 172
引用自:http://c-faq.com/lib/randrange.html
【讨论】:
【参考方案4】:在多次运行程序时,只要使用 rand() 就会给你相同的随机数。即当你第一次运行你的程序时,它会产生随机数 x、y 和 z。如果您再次运行该程序,那么它将产生与我观察到的相同的 x、y 和 z 数。
我发现每次都保持其唯一性的解决方案是使用 srand()
这是附加代码,
#include<stdlib.h>
#include<time.h>
time_t t;
srand((unsigned) time(&t));
int rand_number = rand() % (65 + 1 - 0) + 0 //i.e Random numbers in range 0-65.
要设置范围,您可以使用公式:rand() % (max_number + 1 - minimum_number) + minimum_number
希望对你有帮助!
【讨论】:
【参考方案5】:2 cents (ok 4 cents):
n = rand()
x = result
l = limit
n/RAND_MAX = x/l
Refactor:
(l/1)*(n/RAND_MAX) = (x/l)*(l/1)
Gives:
x = l*n/RAND_MAX
int randn(int limit)
return limit*rand()/RAND_MAX;
int i;
for (i = 0; i < 100; i++)
printf("%d ", randn(10));
if (!(i % 16)) printf("\n");
> test
0
5 1 8 5 4 3 8 8 7 1 8 7 5 3 0 0
3 1 1 9 4 1 0 0 3 5 5 6 6 1 6 4
3 0 6 7 8 5 3 8 7 9 9 5 1 4 2 8
2 7 8 9 9 6 3 2 2 8 0 3 0 6 0 0
9 2 2 5 6 8 7 4 2 7 4 4 9 7 1 5
3 7 6 5 3 1 2 4 8 5 9 7 3 1 6 4
0 6 5
【讨论】:
【参考方案6】:只是为现有答案添加一些额外的细节。
mod %
操作将始终执行完全除法,因此产生的余数小于除数。
x % y = x - (y * floor((x/y)))
使用 cmets 进行随机范围查找函数的示例:
uint32_t rand_range(uint32_t n, uint32_t m)
// size of range, inclusive
const uint32_t length_of_range = m - n + 1;
// add n so that we don't return a number below our range
return (uint32_t)(rand() % length_of_range + n);
上述另一个有趣的属性:
x % y = x,如果 x
const uint32_t value = rand_range(1, RAND_MAX); // results in rand() % RAND_MAX + 1
// TRUE for all x = RAND_MAX, where x is the result of rand()
assert(value == RAND_MAX);
result of rand()
【讨论】:
【参考方案7】:rand() % (max_number + 1 - minimum_number) + minimum_number
所以,对于 0-65:
rand() % (65 + 1 - 0) + 0
(显然您可以不使用 0,但它的存在是为了完整性)。
请注意,这会稍微偏向随机性,但如果您没有做特别敏感的事情,则可能无需担心。
【讨论】:
不,仍然是 number_of_numbers,但 0-65 范围内的数字数量是 66 而不是 65。 我认为当我发表评论时说“rand() % 65 + 0”。现在看起来不错。 确实是这么说的。我的意思是我在第二个代码 sn-p 中写的数字有误,但我在第一个代码 sn-p 中给变量的名称没有弄错。 Modulo 依赖于不像高位那样随机的低位。转换为浮点数和乘法效果更好。 低位随机性较小。此外,number_of_numbers
的值越小,偏差越明显。与 RAND_MAX 无关。例如,如果number_of_numbers
为 2,您将在最低位看到偏差。【参考方案8】:
如果您关心随机数的质量,请不要使用 rand()
使用其他一些 prng,例如 http://en.wikipedia.org/wiki/Mersenne_twister 或其他高质量的 prng 之一
那么就用模数吧。
【讨论】:
【参考方案9】:正如其他人所指出的那样,简单地使用模数会扭曲单个数字的概率,因此更倾向于使用较小的数字。
在 Java 的 java.util.Random
类中使用了一个非常巧妙且很好的解决方案:
public int nextInt(int n)
if (n <= 0)
throw new IllegalArgumentException("n must be positive");
if ((n & -n) == n) // i.e., n is a power of 2
return (int)((n * (long)next(31)) >> 31);
int bits, val;
do
bits = next(31);
val = bits % n;
while (bits - val + (n-1) < 0);
return val;
我花了一段时间才理解它为什么起作用,我把它留给读者作为练习,但这是一个非常简洁的解决方案,可以确保数字具有相等的概率。
那段代码中的重要部分是while
循环的条件,它拒绝落在数字范围内的数字,否则会导致分布不均。
【讨论】:
对不起,看错了;以为它说“使用”Java'c java.util.Random,但您只是指出了该方法。撤回:) 这是一个旧的答案,很抱歉让你烦恼,但这个答案还是有点奇怪,因为问题是用 C 标记的,如果你能花时间添加一个 C 等价的例子,那就太好了代码。例如,next()
是什么? here 一个仍然需要用 C 等效函数替换 next()
的命题。【参考方案10】:
更新为不使用#define
double RAND(double min, double max)
return (double)rand()/(double)RAND_MAX * (max - min) + min;
【讨论】:
坏主意,因为宏的所有警告。该宏在许多情况下都会失败,因为参数没有正确括起来,即使您这样做正确,它仍然会计算 min 参数两次,如果 min 是一个有副作用的函数,那就不好了。跨度> 失去宏方法并转换为双精度。 rand()/RAND_MAX 通常为 0。【参考方案11】:double scale = 1.0 / ((double) RAND_MAX + 1.0);
int min, max;
...
rval = (int)(rand() * scale * (max - min + 1) + min);
【讨论】:
【参考方案12】:在这里查看
http://c-faq.com/lib/randrange.html
对于这些技术中的任何一种,如有必要,可以直接移动范围; [M, N] 范围内的数字可以用类似的东西生成
M + rand() / (RAND_MAX / (N - M + 1) + 1)
【讨论】:
您希望其中一个 1 为 1.0 以确保浮动,否则您只会得到 0 + M... @BrianPostow 不,你肯定不想要一个浮点数。计算是正确的,因为它存在并产生M
和N
(包括)之间的随机数。只要N-M
与RAND_MAX
相比较小,分布就相当均匀。阅读链接的文章。
我是否搞砸了我的实现?似乎这对我公认的小测试用例(在thirdRange()
中实现)非常不利。 repl.it/@altendky/scaling-rand-can-cause-poor-distribution 与RAND_MAX
相比,在N-M
较小的有限情况下,它是否优于评分最高的答案?【参考方案13】:
我认为以下是半正确的。自从我接触 C 以来已经有一段时间了。这个想法是使用除法,因为模数并不总是给出随机结果。我在 RAND_MAX 中加了 1,因为有很多可能的值来自 rand,包括 0。而且由于范围也包括 0,所以我也在那里加了 1。我认为数学排列正确避免了整数数学问题。
#define MK_DIVISOR(max) ((int)((unsigned int)RAND_MAX+1/(max+1)))
num = rand()/MK_DIVISOR(65);
【讨论】:
【参考方案14】:对结果取模,正如其他发帖人所断言的那样,会给你一些几乎随机的东西,但并不完全如此。
考虑这个极端的例子,假设你想模拟掷硬币,返回 0 或 1。你可以这样做:
isHeads = ( rand() % 2 ) == 1;
看起来很无害,对吧?假设 RAND_MAX 仅为 3。当然它要高得多,但这里的重点是,当您使用不均分 RAND_MAX 的模数时存在偏差。如果你想要高质量的随机数,你就会遇到问题。
考虑我的例子。可能的结果是:
rand() freq. rand() % 2
0 1/3 0
1 1/3 1
2 1/3 0
因此,“反面”的出现频率是“正面”的两倍!
先生。阿特伍德讨论此事in this Coding Horror Article
【讨论】:
有一个更好的答案。我现在正在调查。我认为 Atwood 先生大约一年前在 Coding Horror 中讨论过这个问题。 @Sean:将 rand() 的值存储在临时值中。如果它高到足以在错误范围内,请重新滚动。对于 mmyers 给出的示例,如果 rand() 为 3,您将重新滚动。 更好的解决方案是 if(rand() > .5) return 1;否则返回 0; 这里有一个更好的解决方案:***.com/questions/137783/…。上次 akway 询问随机数生成时它就在那里,下次有人问这个确切的问题或数学上等价的问题时它仍然会在那里;-) @James:以概率 RAND_MAX/(RAND_MAX+1) 返回 1,以概率 1/(RAND_MAX+1) 返回 0。不知道应该实现什么。【参考方案15】:天真的做法是:
int myRand = rand() % 66; // for 0-65
这可能是一个非常不均匀的分布(取决于您的最大值),但非常接近。
要解释为什么它不是很统一,请考虑这个非常简化的示例: 假设 RAND_MAX 是 4,并且您想要一个 0-2 之间的数字。您可以获得的可能值如下表所示:
rand() | rand() % 3
---------+------------
0 | 0
1 | 1
2 | 2
3 | 0
看到问题了吗?如果您的最大值不是 RAND_MAX 的偶数除数,您将更有可能选择较小的值。但是,由于 RAND_MAX 通常为 32767,因此偏差可能足够小,可以在大多数情况下逃脱。
有多种方法可以解决这个问题;有关 Java 的 Random
如何处理它的说明,请参阅 here。
【讨论】:
我在你们离开 cmets 的时间里编辑了 两次。 ;) @akway - 它偏向于低值。根据范围,它可能非常偏斜。有关替代方案的详细信息,请参阅我在回答中引用的文章。 从你的例子看来我可以丢弃所有以 0 结尾的结果,我会没事的......还是我错了? @akway:如果示例中 RAND_MAX 为 5 会发生什么? 3->3, 4->0?不确定是否诚实。【参考方案16】:rand() 将返回介于 0 和 RAND_MAX(至少为 32767)之间的数字。
如果你想得到一个范围内的数字,你可以使用模数。
int value = rand() % 66; // 0-65
如需更准确,请check out this article。它讨论了为什么模不一定是好的(不好的分布,特别是在高端),并提供了各种选项。
【讨论】:
【参考方案17】:如果您不太关心低位的“随机性”,只需 rand() % HI_VAL。
还有:
(double)rand() / (double)RAND_MAX; // lazy way to get [0.0, 1.0)
【讨论】:
所以是 0 还是 1?无论如何我可以将它们链接在一起并将结果数字从二进制转换为十进制吗?这会增加很多随机性......我认为。 @akway:不。除法给出了一个介于 0.0 和 1.0 之间的浮点数。(int)(HI_VAL * ( (double)rand() / (double)RAND_MAX ))
通常做你想做的事。
提示:任何时候你为伪随机数生成器“添加”随机性,很可能你无意中减去了它......或者至少没有改变它。
(double)rand() / (double)RAND_MAX 给你一个介于 0 和 1 之间的双精度值,而不是 0 或 1。你可以将这些链接起来,但请记住,并非所有生成器都能正常工作那样。有些相邻值之间存在依赖关系。
哦,好吧,这更有意义。以上是关于如何从 rand() 获取特定范围的数字?的主要内容,如果未能解决你的问题,请参考以下文章
在 postgres 中,如何从事件日志类型表(具有时间戳)中获取特定时间范围内字段的总和(或汇总)
如何从当前日期 PHP 获取最近 7 周、7 个月的日期范围?