Is there a way to generate a seed out of a sequence of numbers?
例如,如果Java产生伪随机序列:9 3,2,5,6。用23作为种子,我该怎么做呢?即从序列9 3 2 5 6中取出23。
或者如何为某个序列分配种子?
如果有一个数据库,很容易做到-只需为序列分配一个随机键
1 | INSERT INTO SEQUENCE_TABLE VALUES (RANDOM_KEY, SEQUENCE) |
但是,如果不允许我使用数据库,有没有一个公式可以做这样的事情?
随机数生成器的要点是这是不可能的。SecureRandom在密码学上特别强大,但一般来说,如果您正在编写一个随机数生成器,而这是可能的或很容易的,那么您就错了。
也就是说,Java内置的随机类很可能是不可能的。(不过,SecureRandom是另一回事。)但它需要惊人的数学运算量。
更具体一点:如果存在一个多项式时间算法来做你想做的事情,对于某个特定的伪随机数生成器,那么根据定义,它将无法通过链接的维基百科文章中描述的"下一位测试",因为你可以预测将要生成的下一个元素。
是的,逆向设计一个设计不好的伪随机数发生器的数字流是绝对容易的,例如Java编程语言中的线性同余PRNG实现(EDOCX1,1)。
事实上,只有来自特定生成器的两个值,以及这些值出现的顺序信息,就可以预测整个流。
1 2 3 4 5 6 7 8 9 10 |
这正是为什么使用密码安全的随机数生成器非常关键的原因,这些生成器已经被社区广泛审查,用于需要安全性的实现。
这里有更多关于逆向工程的信息,包括
当然可以恢复java.util.random使用的种子。这篇文章描述了Random线性同余公式背后的数学原理,这里有一个函数可以从nextint()返回的最后两个整数中发现当前种子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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开头的伪随机数字序列。
您想取任意的数字序列,然后确定一个短的(固定长度?)允许您重新生成该数字序列而不存储原始序列的键?不幸的是,你想要的在技术上是不可能的。这就是为什么:
这是压缩的一种特殊情况。您有一个很长的数据序列,您希望能够从一个较小的信息片段中重新创建无损的数据序列。如果您请求的是可能的,那么我可以将整个堆栈溢出压缩为一个整数(因为整个网站可以序列化为一个数字序列,尽管这个序列非常长!)
不幸的是,数学不是这样的。任何给定的序列都有一个特定的熵度量——该序列的平均复杂性。为了无损地重现这个序列,您必须能够编码至少足够的信息来表示它的熵。
对于某些序列,可能实际上有一个种子能够生成一个长的、特定的序列,但这仅仅是因为有一个硬编码的数学函数,它接受该种子并生成一个特定的数字序列。但是,要获取任意值序列并生成这样的种子,您需要一个种子,以及一个能够从该种子生成该序列的函数。为了对这两样东西进行编码,你会发现你得到的数据比预期的要多得多!
如果您可以使用
1 |
然后你的发电机看起来像:
1 |
如果你真的想在Java中逆向设计"随机数"生成器,那将是非常困难的(我想)。
如果可以的话,最好换一种方式来做:从种子开始,产生序列,然后从那里开始计算。