正如其他人所说,安全 RNG 的吞吐量可能有限。为了缓解这种情况,您可以通过播种 CPRNG 来扩展安全随机性,或者您可以尝试优化比特流的使用。
例如,要洗一副牌,您只需要 226 位,但简单的算法(调用nextInt(n)
每张牌)可能会使用 1600 或 3200 位,浪费 85% 的熵,并使您受到延迟的影响增加七倍。
对于这种情况,我认为雅克医生的方法比较合适。
为此,这里有一些针对成本逐渐增加的熵源的性能分析(还包含代码):
我倾向于高效使用而不是拉伸,因为我认为证明可信赖熵流的高效消费者的公平性比使用种子良好的 PRNG 证明任何绘图方法的公平性要容易得多。
编辑2: 我真的不了解Java,但我把它放在一起:
public class MySecureRandom extends java.security.SecureRandom {
private long m = 1;
private long r = 0;
@Override
public final int nextInt(int n) {
while (true) {
if (m < 0x80000000L) {
m <<= 32;
r <<= 32;
r += (long)next(32) - Integer.MIN_VALUE;
}
long q = m / n;
if (r < n * q) {
int x = (int)(r % n);
m = q;
r /= n;
return x;
}
m -= n * q;
r -= n * q;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这消除了贪婪的默认统一 [0,n-1] 生成器,并将其替换为修改后的 Doctor Jacques 版本。对洗牌值范围进行计时显示,速度比SecureRandom.nextInt(n)
.
我之前版本的这段代码(仅加速了 2 倍)假设这SecureRandom.next(b)
是高效的,但事实证明该调用正在丢弃熵并拖累整个循环。这个版本管理自己的分块。