为什么C ++ rand()似乎只生成相同数量级的数字?


146

在一个用C / C ++编写的小型应用程序中,我遇到了rand函数以及种子的问题:

我想产生一个具有不同阶数的随机数序列,即具有不同的对数值(以2为底)。但是似乎所有产生的数字都是相同的阶次,仅在2 ^ 25和2 ^ 30之间波动。

是因为rand()现在使用Unix时间是一个相对较大的数目吗?我忘记了什么?我rand()刚开始播种一次main()


7
FWIW是C还是C ++?如果使用C / C ++,您的意思是您实际上可以使用C ++,而对C的提及只是随机的,那么也许en.cppreference.com/w/cpp/numeric/random/binomial_distribution可以提供帮助。
R. Martinho Fernandes

9
不幸的是,你押错了马。种子不应该是您的问题。您的问题是预期的分配错误。由于没有偏见的程序员会期望rand()返回均匀分布的数字(Google排名很高的文档明确指出了这一点),所以我认为这个问题对将来的读者没有用。这就是为什么投反对票,但不要让它阻止您使用SO。
Orionii皇帝

12
@ doug65536“ ...没有重复的数字”-这不是随机的!如果我的rand()骰子在返回所有可能的数字之前从未两次返回相同的数字,则可以在掷骰子桌上为退休提供资金。
克里斯·格雷格

6
@GalacticCowboy不要将重复的数字误认为周期性。您从Wikipedia文章中引用:“重复的结果并不意味着已经到了周期的终点,因为其内部状态可能大于其输出。” 如果PRNG产生了一个值,然后保证在返回所有值之前不再次产生该值,那将非常非常糟糕。
克里斯·格雷格

12
Doug65536,没有人在吵架。他们只是在正确说明您错了。如果我想要1到10之间的RAND,PRNG可能会很高兴推出以下内容:2 4 7 2 8 1 5 9 7 3尽管2和7的倍数很大,这完全是有效的。我认为您将PRNG与iPhone上的随机播放功能混淆了。
在塞浦路斯放松

Answers:


479

1和2 30之间只有3%的数字不在2 25和2 30之间。所以,这听起来很正常:)

因为2 25 /2 30 = 2 -5 = 1/32 = 0.03125 = 3.125%


36
是的,很好!2 ^ 25和2 ^ 30之间的数字比1和2 ^ 25之间的数字多31倍:)感谢您的快速回答。那我需要重新考虑程序。问题已回答。
Tallaron Mathias

1
@TallaronMathias考虑通过移位将数字截断>>-这将为您提供较小的数字。(或采用%。取模)
肖恩·阿雷德

13
我希望这对于大多数程序员来说都是显而易见的:任何小于2 ^ 25的无符号整数必须其前7位等于0-并且如果每个位都是随机的...
BlueRaja-Danny Pflughoeft 2013年

118
@ BlueRaja-DannyPflughoeft-如果概率很明显,赌场将倒闭。
Brett Hale

26
@BrettHale-我不认为程序员是赌场的目标人群。
EkoostikMartin 2013年

272

较浅的绿色是0到2 25之间的区域;较深的绿色是2 25和2 30之间的区域。刻度是2的幂。

分配


42

您需要更加精确:您想要不同的以2为底的对数值,但是您想要什么分布?标准rand()函数生成均匀分布,您将需要使用分位数来转换此输出与所需分布相关联函数。

如果您告诉我们分布情况,那么我们可以告诉您所需的quantile功能。


13
+1,分配是关键。当关于分布的信息一无所知时,谈论随机数真的没有任何意义。制服只是一个特例,尽管很重要。可能是指出C ++ 11标准库中各种发行版的好地方。
6

18

如果您想要不同的数量级,为什么不简单尝试pow(2, rand())?还是像Harold建议的那样直接作为rand()选择订单?


3
好主意,但您应该使用pow而不是^(这是C语言中的逻辑异或运算符,而不是幂)来修正答案。
kriss 2013年

6
既然rand()可以达到RAND_MAX,您确实需要缩放您的随机数,这样结果才不会溢出...
Floris 2013年

@Floris:但是,如果在很小的范围内缩放很小的可数范围,则会有很多孔,这可能不是OP期望的。
安德烈·卡伦

13

@ 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,可以执行以下操作:

  1. 生成一个从0到n的小随机数

  2. 如果您知道n所具有的范围,则生成10 ^ k的大随机数,其中k> max {n}。

  3. 剪切较长的随机数以获得该大随机数的n位数字。


46
您是完全正确的,但是对于一个真正易于理解的答案,OP应该问自己为什么1至100之间的随机数中90%是两位数。
询问Monica

13

上面已经给出并接受了基本(正确的)答案:在10到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倍,因为数字增加了一倍-与对数分布不同。更新的答案带有图片。
弗洛里斯2013年

不,我的意思是,有了rand()>>(rand()&31);,人们会直观地期望数字的1/32具有32位,数字的1/32具有31位,数字的1/32具有30位,依此类推。不是得到的结果,只有大约三分之一的数字会产生32位,而几乎一半应为0。由于我的心理数学与您的测量结果不一致,因此我必须自己进行测量才能得出这个出来。
Mooing Duck 2013年

2
我并不是说您的代码是错误的。这可能是我会做的。它只是值得警告,结果并未如人们预期的那样完全分散。
Mooing Duck 2013年

1
我认为问题出在将0视为一个1位数...这是您在混合整数和对数时遇到的难题。不过,这是一个很好的练习,您给了我一些思考。“测试算法的极限”-它永不过时。
Floris

5

0和2 ^ 29以及2 ^ 29和2 ^ 30之间的数字完全相等。

解决问题的另一种方法:考虑生成的随机数的二进制表示形式,即最高位为1的概率等于1/2,因此,在一半情况下获得29阶。您想要的是看到一个小于2 ^ 25的数字,但是这意味着5个最高位全为零,发生的可能性很低,为1/32。即使您长时间运行它,也可能根本看不到低于15的顺序(概率就像连续6次滚动6次)。

现在,关于种子的问题部分。不,种子无法确定生成数字的范围,它只能确定第一个初始元素。将rand()视为范围内所有可能数字的序列(预定排列)。种子确定从序列中开始绘制数字的位置。这就是为什么如果想要(伪)随机性,则使用当前时间来初始化序列:您不必担心起始位置不是均匀分布的,重要的是永远不要从同一位置开始。


2

使用 pow(2,rand()) 它会给出所需幅度的答案!!


2

如果要使用在线服务中的随机数,可以使用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。请不要发布链接作为答案。您可以提供答案的详细草图,以便通过链接阅读详细信息。
Shai 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.