【问题标题】:The behavior of rand() within a loop conditionrand() 在循环条件中的行为
【发布时间】:2023-03-30 07:43:01
【问题描述】:

我试图更好地理解包含某种 rand() 作为其结束条件的循环的行为。考虑以下示例:

for(int i=0; i < rand()%6+10; i++) { do something }

在这种情况下,循环将迭代 10 到 15 次。 我的问题是:终止值是否会被评估一次,然后在其余的迭代中重用?还是会在每次迭代时重新评估?

此外,这是否被认为是不好的做法?

【问题讨论】:

  • 在不知道您的意图的情况下,不可能将其视为“不良做法”。这是否是“不好的做法”取决于您希望这段代码做什么。循环条件在每次迭代中进行整体评估。如果这就是你所需要的,那么你的代码就很好了。如果&lt; 右侧的表达式总是计算为相同的值,则代码将毫无意义。但是rand() 不会计算到相同的值,这意味着上面的代码并不是没有意义的。如果您确实想要在每次迭代时重新生成完成条件,那么代码就完美了。
  • 考虑for (int i = 0, i_end = blah; i != i_end; i++)

标签: c++ loops random iteration


【解决方案1】:

在每次迭代之前对条件进行全面评估。 (想一想:编译器如何知道您每次都想要评估哪些部分,以及您不想评估哪些部分?)

这是否是不好的做法取决于情况。在循环期间终止条件可能发生变化的情况下,这是有道理的。如果您想循环随机次数,那么您应该预先评估终止值,否则您最终会得到与您认为的不同的随机分布。

如果您在每次迭代中评估rand(),那么满足终止条件的几率将根据i 的值在每次迭代中改变,并且您会发现i 的一些终止值与均匀分布。话虽如此,在您的情况下,即使您提前对其进行评估,分布也不会是均匀的,因为(1)rand() 不能保证产生具有均匀分布的值,据我所知,以及(2)采取范围不能被模数整除的随机函数的模数会使分布偏斜。随机函数的范围越大,给定特定模数的偏斜就越少,但它仍然存在。

【讨论】:

  • (想一想:编译器如何知道您每次要评估哪些部分,哪些部分您不需要?):嗯,int i=0; 不是每次迭代都执行,但 i++; 是。因此,我想知道条件是像初始化一样执行一次,还是像入罪一样重复执行。但你肯定回答了我的问题,为此我很感激:)
  • @Gil 好吧,标准中是这么说的。见 6.5.3:[ Note: Thus the first statement specifies initialization for the loop; the condition (6.4) specifies a test, made before each iteration, [...]
  • @remyabel 在哪里可以看到这个标准?
  • @GilDekel:在isocpp.org 网站上,查看左上角,有一个指向标准工作草案的链接。
  • @cdhowie:假设从rand() % 6均匀分布,在[10..15]范围内i的每个值的完成概率如下:0.1(6)0.2(7)0.2(7)0.(185)0.07716049...0.0115432...。这意味着i 最可能的最终值是1112。因此,您断言循环将以更少的迭代次数更频繁地退出并不完全准确:10 的最终值出现的频率将低于1112
【解决方案2】:

C++ 标准规定 for 声明:

for ( <em>for-init-statement condition</em><sub>opt</sub> ; <em>expression</em><sub>opt</sub> ) <em>statement</em>

相当于:

{ for-init-statement while ( condition) { statement expression ; } }

[ 注意:因此第一条语句指定了 环形;条件 (6.4) 指定了一个测试,在每个测试之前进行 迭代,[...]

所以是的,每次迭代都会重新评估条件。

至于使用rand() 是否是不好的做法……rand() 函数取决于库和平台。在 linux 上,您可以通过 man 3 rand 找到文档。例如,online man page 表示:

不要在旨在可移植的应用程序中使用此功能 需要良好的随机性。

还有cppreference's opinion on std::rand:

不保证随机序列的质量 产生。过去,rand() 的一些实现有 在随机性、分布和周期方面存在严重缺陷 产生的序列(在一个众所周知的例子中,低位 在调用之间交替在 1 和 0 之间)。

rand() 不推荐用于严重的随机数生成需求, 像密码学。推荐使用C++11的random number generation facilities 替换rand()(C++11 起)

如果您想深入了解如何使用这些 C++11 功能,请咨询Random number generation in C++11, how to generate, how do they work?

有关rand() 的各种用法以及它们为什么不好的分析,请参阅Julienne Walker's article

例如:

1 int r = rand() % N;

要改为添加下限并将范围设置为 [M..N),可以这样做 这个:

1 int r = M + rand() % ( N - M );

任何这样做的人都将获得看似随机的奖励 并为他们聪明的解决方案奏效而感到兴奋。不幸的是,这 不起作用。第一个解决方案仅在 N 均分时有效 进入 RAND_MAX。第二种解决方案也好不到哪里去。原因是 因为以这种方式强制范围消除了任何机会 均匀分布。现在,这没关系,如果你什么都不在乎 有些数字比其他数字更有可能,但要正确,你 必须使用分发而不是销毁它。

【讨论】:

  • C++ 标准对于答案顶部的等效性并不完全正确,尤其是在涉及 continue; 的行为方式时。
  • 感谢您提供的精彩参考!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 2015-10-11
  • 2014-03-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多