例如,如果Java产生伪随机序列:9 3,2,5,6。用23作为种子,我该怎么做呢?即从序列9 3 2 5 6中取出23。
或者如何为某个序列分配种子?
如果有一个数据库,很容易做到-只需为序列分配一个随机键
INSERT INTO SEQUENCE_TABLE VALUES (RANDOM_KEY, SEQUENCE)
但是,如果不允许我使用数据库,有没有一个公式可以做这样的事情?
据我所知,不可能从输出中确定种子,除非事先有一个每个可能的种子/序列对的查找表。你的用例是什么?可能有比试图逆转Java的PRNG更优雅的解决方案。
我只需要为一系列唯一的数字分配一个键,但是这个键也必须是短的。密钥只是表示序列的一种简短方式,比如Java中的种子来表示伪随机数序列。
我只是想用Java的种子和随机函数来再生我独特的数字序列。如果有更简单的方法,希望你能提出建议并感谢你。
我不确定你到底想达到什么目的。你能详细阐述一下你的问题吗?您将收到哪些输入,您需要生成什么?
您将:(1)根据RNG算法的需求生成一个种子。(2)将该种子与一个序列(或数字的子序列,包括起始位置)一起保存在数据库中;(3)根据需要通过使用相同的值重新播种来复制流。如果您试图反向工程RNG输出,请参阅下面的答案。
随机数生成器的要点是这是不可能的。SecureRandom在密码学上特别强大,但一般来说,如果您正在编写一个随机数生成器,而这是可能的或很容易的,那么您就错了。
也就是说,Java内置的随机类很可能是不可能的。(不过,SecureRandom是另一回事。)但它需要惊人的数学运算量。
更具体一点:如果存在一个多项式时间算法来做你想做的事情,对于某个特定的伪随机数生成器,那么根据定义,它将无法通过链接的维基百科文章中描述的"下一位测试",因为你可以预测将要生成的下一个元素。
事实上,java.util.random并不重要,请看我的文章。
是的,逆向设计一个设计不好的伪随机数发生器的数字流是绝对容易的,例如Java编程语言中的线性同余PRNG实现(EDOCX1,1)。
事实上,只有来自特定生成器的两个值,以及这些值出现的顺序信息,就可以预测整个流。
Random random = new Random();
long v1 = random.nextInt();
long v2 = random.nextInt();
for (int i = 0; i < 65536; i++) {
long seed = v1 * 65536 + i;
if (((seed * multiplier + addend) & mask) >>> 16) == v2) {
System.out.println("Seed found:" + seed);
break;
}
}
这正是为什么使用密码安全的随机数生成器非常关键的原因,这些生成器已经被社区广泛审查,用于需要安全性的实现。
这里有更多关于逆向工程的信息,包括java.util.Random。…
PS,这不应该被用作体系结构特性。我只是把这个贴在这里让大家知道。
当然可以恢复java.util.random使用的种子。这篇文章描述了Random线性同余公式背后的数学原理,这里有一个函数可以从nextint()返回的最后两个整数中发现当前种子。
public static long getCurrentSeed(int i1, int i2) {
final long multiplier = 0x5DEECE66DL;
final long inv_mult = 0xDFE05BCB1365L;
final long increment = 0xBL;
final long mask = ((1L << 48) - 1);
long suffix = 0L;
long lastSeed;
long currSeed;
int lastInt;
for (long i=0; i < (1<<16); i++) {
suffix = i;
currSeed = ((long)i2 << 16) | suffix;
lastSeed = ((currSeed - increment) * inv_mult) & mask;
lastInt = (int)(lastSeed >>> 16);
if (lastInt == i1) {
/* We've found the current seed, need to roll back 2 seeds */
currSeed = lastSeed;
lastSeed = ((currSeed - increment) * inv_mult) & mask;
return lastSeed ^ multiplier;
}
}
/* Error, current seed not found */
System.err.println("current seed not found");
return 0;
}
此函数返回一个值,该值可与rand.setseed()一起使用,生成以i1和i2开头的伪随机数字序列。
您想取任意的数字序列,然后确定一个短的(固定长度?)允许您重新生成该数字序列而不存储原始序列的键?不幸的是,你想要的在技术上是不可能的。这就是为什么:
这是压缩的一种特殊情况。您有一个很长的数据序列,您希望能够从一个较小的信息片段中重新创建无损的数据序列。如果您请求的是可能的,那么我可以将整个堆栈溢出压缩为一个整数(因为整个网站可以序列化为一个数字序列,尽管这个序列非常长!)
不幸的是,数学不是这样的。任何给定的序列都有一个特定的熵度量——该序列的平均复杂性。为了无损地重现这个序列,您必须能够编码至少足够的信息来表示它的熵。
对于某些序列,可能实际上有一个种子能够生成一个长的、特定的序列,但这仅仅是因为有一个硬编码的数学函数,它接受该种子并生成一个特定的数字序列。但是,要获取任意值序列并生成这样的种子,您需要一个种子,以及一个能够从该种子生成该序列的函数。为了对这两样东西进行编码,你会发现你得到的数据比预期的要多得多!
这是完全错误的。CSPRNGS和PRNGS之间存在差异,而CSPRNGS的设计正是为了避免这种困境。一旦已知RNG的初始种子(按照RNG周期的顺序,或者对于设计不好的算法来说,小于等于RNG周期的顺序),序列中的所有数字都是确定已知的。en.wikipedia.org/wiki/random_number_generator_攻击
@嗯,我想你可能还没有读完整的原始问题。作者不是在问他是否能从一个序列中产生数字,如果他知道密钥,他是在问是否有一种方法(不列举每一个可能的选项)来识别一个数字密钥可以无损地产生任何随机的数字序列。
如果您可以使用String作为种子,则可以使用:
String seed ="9 3 2 5 6";
然后你的发电机看起来像:
String[] numbers = seed.split("");
如果你真的想在Java中逆向设计"随机数"生成器,那将是非常困难的(我想)。
如果可以的话,最好换一种方式来做:从种子开始,产生序列,然后从那里开始计算。
我也想过这样做,但是,如果序列太长,这将是一个问题。谢谢你的建议