我记得有关在数学导向网站上的文章中有效使用随机位的方法,但我似乎无法在Google中获得正确的关键字找到它了,它不在我的浏览器历史记录中。按位高效,统一,加密安全的随机数生成
正被提出问题的要点是采取随机数的序列中的结构域[domainStart
,domainEnd
)和有效地使用该随机数序列的比特均匀地伸入范围[rangeStart
,rangeEnd
) 。域和范围都是整数(更准确地说,是long
而不是Z)。 这是什么算法?
实现的角度来看,我有与此签名的函数:,我需要使用
long doRead(InputStream in, long rangeStart, long rangeEnd);
in
是基于CSPRNG(由硬件RNG,通过SecureRandom的空调供给);返回的值必须是rangeStart
和rangeEnd
之间,但这种明显的实现是一种浪费:
long doRead(InputStream in, long rangeStart, long rangeEnd) {
long retVal = 0;
long range = rangeEnd - rangeStart;
// Fill until we get to range
for (int i = 0; (1 << (8 * i)) < range; i++) {
int in = 0;
do {
in = in.read();
// but be sure we don't exceed range
} while(retVal + (in << (8 * i)) >= range);
retVal += in << (8 * i);
}
return retVal + rangeStart;
}
我相信这是实际上是相同的想法亨利指出,这个代码偏向0和257;班塔尔在一个例子中演示了它。(rand() * (max - min)) + min
,只有我们丢弃它可以让我们在max
位。我们丢弃这些位并重试,而不是使用可能错误地将结果偏置到较低值的模运算符。由于触发CSPRNG可能会触发重新播种(可能会阻塞InputStream),因此我想避免浪费随机位。
首先编辑:亨利提醒我,求和调用中心极限定理。我修正了上面的代码来解决这个问题。
第二次编辑:机械蜗牛建议我查看Random.nextInt()的源代码。在阅读了一段时间之后,我意识到这个问题与基本转换问题类似。见下面的答案。
“明显的实现”不仅浪费,而且在概念上也是错误的(除了一些实现错误)。通过添加随机数字,您可以更改分配。如果添加足够的数字,它将变成高斯。例如,投掷两个骰子会比2多产生7次。 – Henry
谢谢。我知道我在算法上做了一些非常错误的事情。 :我应该睡一会儿。 – user314104
看看java.util.Random.nextInt的实现。 –