我有一个需要较长随机位序列的应用程序。
因为我依赖这些序列是“真正随机的”,所以我想使用SecureRandom。
遗憾的是,每个这样的序列我都需要两次。
我无法将它们保留在RAM中,因为它们太长了。
我不想将它们写入硬盘驱动器,因为向硬盘驱动器写入和读取会浪费时间和硬盘驱动器空间,并使代码不必要地变得复杂。
但是,由于某些生成器生成的“随机”位序列确定性地取决于生成器在创建时所处的状态,因此一个(也许)可以保存并重新创建新生成的SecureRandom对象所处的状态。 ,允许增加位序列。
主要问题是:
(如何)可以保存SecureRandom随机数生成器的整个状态?克隆够了吗? (可能会有一些OS状态也起作用,例如当前时间)。您还可以克隆其他强大的RNG以便原始副本和克隆副本创建相同的输出吗?
此外,我对正常的随机数生成器会创建哪种类型的模式,使其变得不安全以及是否可以如上所述保存和恢复其状态感兴趣。
编辑:
我为此创建了一个LCG。
但是,我认为它无法正常工作。
它会创建一个“强”的伪随机位序列吗?
当它连续多次产生相同的值时,就变得可疑了。 例如:
假 假 真正 真正 真正 真正 假 真正 假 假 假 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 真正 是
public class DeterministicRandom{
private BigInteger state;
private BigInteger a;
private BigInteger c;
private static final int o=32;
private static final BigInteger m=BigInteger.ONE.shiftLeft(o*8);
public DeterministicRandom(){
a=RandomUtils.randomBigInt(o);
c=RandomUtils.randomBigInt(o);
state=RandomUtils.randomBigInt(o);
}
public DeterministicRandom(DeterministicRandom r){
a=r.a;
c=r.c;
state=r.state;
}
public boolean nextBoolean(){
boolean r=state.testBit(o*8-7);
state=state.multiply(a).add(c).mod(m);
return r;
}
}
public class RandomUtils {
private static Random random = new Random();
public static byte[] nextBytes(byte[] d){
random.nextBytes(d);
return d;
}
public boolean randomBit(){
return random.nextBoolean();
}
public static BigInteger randomBigInt(int bytes){
byte[] d=new byte[bytes+1];
random.nextBytes(d);
d[0]=0;
BigInteger x=new BigInteger(d);
return x;
}
}
答案 0 :(得分:3)
似乎您需要用于机器学习或蒙特卡洛采样的随机数。您似乎还考虑了java.util.Random
,但发现它不适合您的目的。在这种情况下,SecureRandom
远不是最佳选择。特别是,没有实现SecureRandom
的“ SHA1PRNG”提供程序的标准方法,并且在应用程序关心可重现的“随机性”的情况下,不应使用SecureRandom
。另请参见Encryption algorithm giving different results on Android 2.1 and versions above 2.1。
相反,您需要一种高质量PRNG ,并且有许多high-quality RNG algorithms可用。
在Java中,两个examples包含it.unimi.dsi/dsiutils
和org.apache.commons/commons-rng-simple
工件,其中包括可植入PRNG的实现,例如xoroshiro128++
和xoshiro256**
。
答案 1 :(得分:2)
我认为您一般不能依赖 来克隆SecureRandom
实例,也不能依赖提供与原始序列相同的克隆。
问题是典型的Java平台提供了安全随机性的多种实现,并且当您调用new SecureRandom()
时会获得默认的实现。这可能是PRNG,也可能是使用“真实”随机性源的RNG。在后一种情况下,即使类支持clone()
,克隆对象也不会给出与原始对象相同的随机序列。
我通常会想到几种解决方案。
第一个解决方案是创建包装Random
实例的自己的SecureRandom
类。特殊的调味料是您的包装器类需要记录它发出的随机数流。然后,您需要一种reset()
方法,使PRNG切换为使用记录的号码。
缺点是您需要足够的内存来容纳所有数字。 (取决于表示形式,这可能会占用很多内存。例如,如果您在ArrayList<Integer>
中记录N个整数,则最坏的情况是Integer
对象的N x 24字节,峰值为3 ArrayList
的x N x 8个字节。int[]
的密度更高,但更难管理。)
我看到您已经说过序列太长而无法保存在您的用例中。您可以考虑将它们写入文件,但是即使那样也不会无限扩展。
第二种解决方案是利用SecureRandom
类的能力来选择算法并重新设定种子本身。
SecureRandom seeder = new SecureRandom();
byte[] seed = seeder.generateSeed(NOS_BYTES);
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
prng.setSeed(seed);
SecureRandom prng2 = SecureRandom.getInstance("SHA1PRNG");
prng2.setSeed(seed);
// prng1 and prng2 created as above should generate the same sequences
将NOS_BYTES
设置为要使用的种子的字节数。或者,您可以“手动”生成种子,也可以从文件中读取它。
请注意,您不能像使用new SecureRandom()
获得的实例那样可靠地设置种子,也不能可靠地重新种子现有实例 1 。
根据配置的Java平台安全提供程序,可能还会使用其他PRNG算法。
有关更多信息,请参阅SecureRandom
javadoc。
我测试了上述方法,并在使用Java 11的计算机上,prng
和prng2
对象生成了相同的int
值序列...直到我击中^ C。 / p>
1-问题的症结在于setSeed
的javadoc这样说:“给定的种子补充而不是替换了现有的种子。” 。在其他地方,它表示构造函数返回已播种的对象。幸运的是,它还说getInstance
方法返回了尚未种子的对象。
最后,我从您更新的问题中看到,您正在考虑使用通过BigInteger
实现的“自行拥有” PRNG。
当心!
实现不如您想像的PRNG非常容易。我不会那样做。但是,如果您决定走这条路,那么您应该通读PRNG的理论及其属性,并对实现进行一系列(相关的)统计测试。
(如果您打算发表结果,您的论文应该提到您实现了自己的PRNG,并且应该使PRNG源代码和统计测试结果可供读者检查。出于公开和科学的考虑可复制性。我认为。)