我无法理解这个,哪个更随机?

rand()

OR:

rand() * rand()

我发现这是一个真正的脑筋急转弯,你能帮我吗?


编辑:

直觉上,我知道数学上的答案是它们是同样随机的,但我不禁想,如果你在将两者相乘时“运行随机数算法”两次,你会创建比只做一次更随机的东西。


当前回答

我猜这两种方法都是随机的,尽管我的直觉会说rand()*rand(()不那么随机,因为它会产生更多的零。一旦一个rand()为0,总数即为0

其他回答

这不是很明显,但rand()通常比rand(*rand)更随机。重要的是,对于大多数用途来说,这实际上不是很重要。

但首先,它们产生了不同的分布。如果这是你想要的,这不是问题,但这很重要。如果你需要一个特定的分布,那么忽略整个“哪个更随机”的问题。那么为什么rand()更随机呢?

rand()之所以更随机(假设它产生的是[0..1]范围内的浮点随机数,这是非常常见的)的核心是,当你将两个FP数与尾数中的大量信息相乘时,你会在结尾处丢失一些信息;IEEE双精度浮点中没有足够的位来保存从[0..1]中均匀随机选择的两个IEEE双精度浮点数中的所有信息,这些额外的信息位将丢失。当然,这无关紧要,因为你(可能)不会使用这些信息,但损失是真实的。您产生哪种分布(即,使用哪种操作进行组合)也并不重要。这些随机数中的每一个都有(最多)52位随机信息——这就是IEEE双精度的容量——如果你将两个或多个随机数合并为一个,那么你仍然只能拥有最多52位的随机信息。

大多数随机数的使用甚至没有使用随机源中实际可用的那么多随机性。得到一个好的PRNG,不要太担心它。(“好”的程度取决于你在用它做什么;你在做蒙特卡洛模拟或密码学时必须小心,否则你可能会使用标准PRNG,因为这通常要快得多。)

两者都不是“更随机”的。

rand()基于伪随机种子生成一组可预测的数字(通常基于当前时间,该时间总是在变化)。将序列中的两个连续数字相乘,生成一个不同但同样可预测的数字序列。

关于这是否会减少冲突,答案是否定的。它实际上会增加冲突,这是因为在0<n<1的情况下,两个数字相乘的结果。结果将是一个较小的分数,导致结果偏向频谱的低端。

一些进一步的解释。在下文中,“不可预测”和“随机”是指某人根据先前的数字猜测下一个数字的能力,即预言。

给定生成以下值列表的种子x:

0.3, 0.6, 0.2, 0.4, 0.8, 0.1, 0.7, 0.3, ...

rand()将生成上述列表,rand(*rand)将生成:

0.18, 0.08, 0.08, 0.21, ...

这两种方法将始终为同一种子生成相同的数字列表,因此预言者同样可以预测。但是如果你看一下两个调用相乘的结果,你会发现它们都在0.3以下,尽管在原始序列中分布良好。由于两个分数相乘的影响,这些数字是有偏差的。由此产生的数字总是较小,因此更可能发生碰撞,尽管仍然无法预测。

关于“随机性”的一些事情是反直觉的。

假设rand()的平面分布,下面将得到非平面分布:

高偏差:sqrt(rand(范围^2))中间偏差峰值:(rand(range)+rand(range))/2低:偏差:范围-sqrt(rand(范围^2))

有很多其他方法可以创建特定的偏置曲线。我对rand()*rand(()做了一个快速测试,它得到了一个非常非线性的分布。

公认的答案很好,但有另一种方法可以回答你的问题。PachydermPuncher的答案已经采用了这种替代方法,我只是将其扩展一点。

思考信息理论最简单的方法是用最小的信息单位,一个比特。

在C标准库中,rand()返回一个0到rand_MAX范围内的整数,根据平台的不同,这个限制可能会有不同的定义。假设RAND_MAX恰好被定义为2^n-1,其中n是某个整数(这恰好是Microsoft实现中的情况,其中n为15)。然后我们可以说,一个好的实现将返回n位信息。

想象一下,rand()通过翻转硬币找到一位的值来构造随机数,然后重复直到它有一批15位。然后,这些位是独立的(任何一个位的值都不会影响同一批中其他位具有特定值的可能性)。因此,独立考虑的每个比特都像一个介于0和1之间的随机数,并且在该范围内“均匀分布”(可能是0和1)。

位的独立性确保了由一批位表示的数字也将在其范围内均匀分布。这很明显:如果有15位,允许的范围是0到2^15-1=32767。该范围内的每个数字都是唯一的位模式,例如:

010110101110010

并且如果比特是独立的,则没有模式比任何其他模式更可能发生。因此,该范围内所有可能的数字都有相同的可能性。反之亦然:如果rand()产生均匀分布的整数,那么这些数字是由独立的位组成的。

因此,将rand()看作是一条生产比特的生产线,它恰好以任意大小的批量提供比特。如果您不喜欢大小,请将批分成单独的位,然后按您喜欢的数量将它们放回一起(尽管如果您需要的特定范围不是2的幂,则需要缩小数字,目前最简单的方法是转换为浮点)。

回到你最初的建议,假设你想从15个批次到30个批次,向rand()请求第一个数字,将其移位15位,然后向其添加另一个rand(()。这是一种在不影响均匀分布的情况下组合对rand(的两个调用的方法。它的工作原理很简单,因为放置信息位的位置之间没有重叠。

这与通过乘以常数来“拉伸”rand()的范围非常不同。例如,如果你想将rand()的范围加倍,你可以乘以2,但现在你只能得到偶数,而不能得到奇数!这并不完全是一个平稳的分布,并且可能是一个严重的问题,具体取决于应用程序,例如,假设允许奇数/偶数下注的轮盘游戏。(从位的角度考虑,你可以直观地避免这个错误,因为你会意识到,乘以2等于将位向左移动一位(意义更大),然后用零填补空白。所以很明显,信息量是一样的——只是移动了一点。)

在浮点数应用程序中,数字范围中的这种差距是无法解决的,因为浮点数范围内在地具有根本无法表示的差距:在每两个可表示的浮点数之间的差距中存在无限数量的缺失实数!所以无论如何,我们必须学会与差距共处。

正如其他人所警告的那样,直觉在这一领域是有风险的,特别是因为数学家无法抵抗实数的诱惑,因为实数是一种充满了粗糙的无限和明显的悖论的可怕的令人困惑的东西。

但至少如果你从比特角度来看,你的直觉可能会让你走得更远。比特真的很容易——甚至计算机都能理解。

假设rand()返回一个介于[0,1)之间的数字,很明显rand(*rand)将偏向于0。这是因为将x乘以[0,1)之间的数字将得到一个小于x的数字。下面是10000个随机数的分布:

google.charts.load(“current”,{packages:[“corechart”]});google.cacharts.setOnLoadCallback(drawChart);函数drawChart(){变量i;var randomNumbers=[];对于(i=0;i<10000;i++){randomNumbers.push(Math.rrandom()*Math.random());}var chart=新的google.visability.Histogram(document.getElementById(“chart-1”));var data=新的google.visibility.DataTable();data.addColumn(“数字”,“值”);randomNumbers.forEach(函数(randomNumber){data.addRow([randomNumber]);});chart.draw(数据{title:randomNumbers.length+“rand()*rand(值介于[0,1)之间”,图例:{位置:“无”}});}<script src=“https://www.gstatic.com/charts/loader.js“></script><div id=“chart-1”style=“height:500px”>正在生成图表</分区>

如果rand()返回[x,y]之间的整数,则得到以下分布。注意奇数与偶数的数量:

google.charts.load(“current”,{packages:[“corechart”]});google.cacharts.setOnLoadCallback(drawChart);document.querySelector(“#绘制图表”).addEventListener(“单击”,绘制图表);函数randomInt(最小值,最大值){return Math.floor(Math.random()*(max-min+1))+min;}函数drawChart(){var min=编号(document.querySelector(“#rand min”).value);var max=编号(document.querySelector(“#rand max”).value);如果(最小值>=最大值){回来}变量i;var randomNumbers=[];对于(i=0;i<10000;i++){randomNumbers.push(randomInt(最小,最大)*randomInt(最小,最小));}var chart=新的google.visability.Histogram(document.getElementById(“chart-1”));var data=新的google.visibility.DataTable();data.addColumn(“数字”,“值”);randomNumbers.forEach(函数(randomNumber){data.addRow([randomNumber]);});chart.draw(数据{title:randomNumbers.length+“rand()*rand(()值介于[“+min+”,“+max+”]”之间,图例:{位置:“无”},直方图:{bucketSize:1}});}<script src=“https://www.gstatic.com/charts/loader.js“></script><input-type=“number”id=“rand-min”value=“0”min=“0“max=“10”><input type=“number”id=“rand max”value=“9”min=“0”max=“10”><input type=“button”id=“draw chart”value=“Apply”><div id=“chart-1”style=“height:500px”>正在生成图表</分区>