为啥 C++ rand() 似乎只生成相同数量级的数字?
Posted
技术标签:
【中文标题】为啥 C++ rand() 似乎只生成相同数量级的数字?【英文标题】:Why does C++ rand() seem to generate only numbers of the same order of magnitude?为什么 C++ rand() 似乎只生成相同数量级的数字? 【发布时间】:2013-06-17 02:06:46 【问题描述】:在一个用 C/C++ 编写的小型应用程序中,我遇到了 rand
函数的问题,也许是种子问题:
我想生成一系列具有不同顺序的随机数,即具有不同的对数值(以 2 为底)。但似乎所有产生的数字都是相同的顺序,只是在 2^25 和 2^30 之间波动。
是不是因为rand()
是用 Unix 时间播种的,而 Unix 时间现在是一个相对较大的数字?我忘记了什么?
我只在main()
的开头播种rand()
一次。
【问题讨论】:
FWIW 那么,它是 C 还是 C++?如果通过 C/C++ 你的意思是你实际上可以使用 C++,而 C 的提及只是随机的,也许这个 en.cppreference.com/w/cpp/numeric/random/binomial_distribution 可以提供帮助。 很遗憾,您赌错了马匹。种子不应该是你的问题。你的问题是错误的预期分布。由于公正的程序员会期望rand()
返回均匀分布的数字(谷歌排名高的文档明确表示)我认为这个问题对未来的读者没有用处。这就是为什么投反对票,但不要让它阻止你使用 SO。
@doug65536 “……没有重复的数字”——这不是随机的!如果我的 rand() 骰子在返回所有可能的数字之前从未两次返回相同的数字,我可以在掷骰子桌上为我的退休提供资金。
@GalacticCowboy 不要将周期性误认为是单个数字的重复。从您引用的***文章中:“重复的结果并不意味着已达到周期结束,因为其内部状态可能大于其输出。”如果 PRNG 产生一个值,然后保证在所有值都返回之前不会再次产生该值,那将是非常非常糟糕的。
Doug65536,没人打架。他们只是正确地说明你错了。如果我想要一个介于 1 和 10 之间的 RAND,PRNG 可以很高兴地产生以下内容: 2 4 7 2 8 1 5 9 7 3 尽管有多个 2 和 7,这将是完全有效的。我认为您将 PRNG 与 iPhone 上的随机播放功能混淆了。
【参考方案1】:
只有 3% 的数字介于 1 和 230 之间,而不是介于 225 和 230 之间。所以,这听起来很正常:)
因为 225 / 230 = 2-5 = 1/32 = 0.03125 = 3.125%
【讨论】:
是的,好点! 2^25 和 2^30 之间的数字是 1 和 2^25 之间的 31 倍 :) 感谢您的快速回答。那我需要重新考虑这个程序。问题已回答。 @TallaronMathias 考虑通过>>
bitshifting 截断数字——这会给你更小的数字。 (或使用%
取模数。)
我希望这对大多数程序员来说是显而易见的:任何小于 2^25 的无符号整数的前 7 位都必须等于 0
- 如果每个位都是随机的......跨度>
@BlueRaja-DannyPflughoeft - 如果概率很明显,赌场就会倒闭。
@BrettHale - 我不认为程序员是赌场的目标人群。【参考方案2】:
较浅的绿色是0到2之间的区域25;较深的绿色是 225 和 230 之间的区域。刻度是 2 的幂。
【讨论】:
【参考方案3】:您需要更精确:您想要不同的以 2 为底的对数值,但您想要什么分布?标准 rand() 函数会生成均匀分布,您需要使用与所需分布相关的 quantile 函数来转换此输出。
如果您告诉我们分布,那么我们可以告诉您您需要的 quantile
函数。
【讨论】:
+1,distribution 是关键术语。当对分布一无所知时,谈论随机数是没有意义的。制服只是一种特殊情况,尽管它很重要。可能是指出 C++11 标准库中各种发行版的好地方。【参考方案4】:如果您想要不同的数量级,为什么不简单地尝试pow(2, rand())
?或者像 Harold 建议的那样直接选择 rand() 的顺序?
【讨论】:
好主意,但您应该使用 pow 而不是 ^(在 C 语言中是逻辑 xor 运算符,而不是幂)来修复您的答案。 由于rand()
可以上升到RAND_MAX
,你真的需要缩放你的随机数,这样结果才不会溢出......
@Floris:但是如果你在一个非常大的范围上缩放一个小的可数范围,你会有很多洞,这可能不是 OP 所期望的。【参考方案5】:
@C4stor 提出了一个很好的观点。但是,对于更一般的情况和更容易理解的人类(以 10 为底):对于从 1 到 10^n 的范围,约 90% 的数字是从 10^(n-1) 到 10^n,因此,约 99% 的数字从 10^(n-2) 变为 10^n。继续添加任意数量的小数。
有趣的数学,如果你对n继续这样做,你可以看到从1到10^n,99.9999...% = 100%这个数字是从10^0到10^n。
现在关于代码,如果你想要一个随机数量级的随机数,从 0 到 10^n,你可以这样做:
生成一个从0到n的小随机数
如果您知道 n 的范围,请生成一个 10^k 阶的大随机数,其中 k > maxn。
截取较长的随机数,得到这个大随机数的n位。
【讨论】:
你是完全正确的,但对于一个非常容易理解的答案,OP 应该问自己为什么 1 到 100 之间的随机数中有 90% 是两位数。【参考方案6】:上面已经给出并接受了基本(正确)答案:0 到 9 之间有 10 个数字,10 到 99 之间有 90 个数字,100 到 999 之间有 900 个等等。
对于获得具有近似对数分布的分布的计算有效方法,您希望将随机数右移一个随机数:
s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift
它并不完美,但它比计算 pow(2, rand()*scalefactor)
快得多。从某种意义上说,它将是“块状”的,因为在因子 2 内的数字分布将是均匀的(128 到 255 是均匀的,256 到 1023 是密度的一半,等等)。
这里是数字 0 到 31 的频率的直方图(在 1M 样本中):
【讨论】:
nitpick:这鼓励了非常小的数字,超出了人们的预期。得零的概率明显高于 10。 嗯 - 这样做的全部目的是鼓励小数字,所以我很高兴它起作用了!我进行了蒙特卡洛模拟,这使我的概率下降了 2 倍,因为数字加倍 - 与对数分布不同。用图片更新了答案。 不,我的意思是,对于rand()>>(rand()&31);
,人们会直观地期望 1/32 的数字具有 32 位,1/32 的数字具有 31 位,1/32 的数字有 30 位等。但这不是你得到的结果,只有大约 1/64 的数字会产生 32 位,而几乎一半应该是 0。因为我的心算与你的测量结果不一致,我必须自己测量才能弄清楚。
我并不是说你的代码是错误的。这可能是我会做的。值得一提的是,结果并没有像预期的那样完全分布。
我认为问题出在将 0 视为 1 位数字......这就是混合整数和对数时遇到的难题。这是一个很好的练习,你给了我一些思考的东西。 “测试算法的极限”——它永远不会过时。【参考方案7】:
在 0 和 2^29 以及 2^29 和 2^30 之间存在完全相同的数字。
另一种看待问题的方式:考虑您生成的随机数的二进制表示,最高位为 1 的概率等于 1/2,因此,在一半的情况下您会得到 29 阶。你想要的是看到一个低于 2^25 的数字,但这意味着 5 个最高位全为零,这种情况发生的概率很低,只有 1/32。很有可能,即使你运行了很长时间,你也永远不会看到低于 15 的顺序(概率类似于连续滚动 6 6 次)。
现在,关于种子的部分问题。不,种子不可能确定生成数字的范围,它只是确定第一个初始元素。将 rand() 视为范围内所有可能数字的序列(预定排列)。种子决定了您从序列中开始绘制数字的位置。这就是为什么如果你想要(伪)随机性,你使用当前时间来初始化序列:你不关心你开始的位置不是均匀分布的,重要的是你永远不会从同一个位置开始。
【讨论】:
【参考方案8】:使用
pow(2,rand())
它会按照期望的数量级给出答案!!
【讨论】:
【参考方案9】:如果您想使用在线服务中的随机数,您可以使用 wget,您可能想查看 您还可以使用 random.org 之类的服务来生成随机数,您可以使用 wget 捕获它们,然后从下载的文件中读取数字
wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt
http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html
【讨论】:
欢迎来到 SO。请不要发布链接作为答案。您可以提供答案的详细草图,留下通过链接阅读的详细信息。以上是关于为啥 C++ rand() 似乎只生成相同数量级的数字?的主要内容,如果未能解决你的问题,请参考以下文章