【问题标题】:Is there a way to generate a seed out of a sequence of numbers?有没有办法从数字序列中生成种子?
【发布时间】:2012-02-14 18:02:32
【问题描述】:

例如,如果 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 算法的 req 生成种子。 (2) 将该种子与序列(或包括起始位置的数字子序列)一起保存在数据库中; (3) 通过重新播种相同的值来按需复制流。如果您尝试对 RNG 输出进行逆向工程,请参阅下面的答案。

标签: java random random-seed


【解决方案1】:

如果您可以使用 String 作为种子,则可以使用:

String seed = "9 3 2 5 6";

那么您的生成器将如下所示:

String[] numbers = seed.split(" ");

如果你真的想对 java 中的“随机”数字生成器进行逆向工程,那将是相当困难的(我认为)。

如果可以的话,最好反过来做:从种子开始,产生序列,然后从那里开始。

【讨论】:

  • 我也想过这样做,但是如果序列太长就会出现问题。感谢您的建议
【解决方案2】:

随机数生成器的要点是这是不可能的。 SecureRandom 被特别设计为cryptographically strong,但一般来说,如果你正在编写一个随机数生成器并且这是可能的或容易的,那么你做错了。

也就是说,Java 内置的 Random 类可能不是不可能。 (不过,SecureRandom 是另一回事。)但这需要大量的数学运算。

更具体地说:如果存在多项式时间算法来执行您想要的操作,对于某些特定的伪随机数生成器,那么根据定义,它会通过链接的 Wikipedia 文章中描述的“下一位测试”失败,因为您可以预测下一个将要生成的元素。

【讨论】:

  • 其实用 java.util.Random 很简单,请看我的帖子。
【解决方案3】:

您想获取任意数字序列,然后确定一个短(固定长度?)密钥,它允许您重新生成该数字序列,而不存储原始数字?不幸的是,你想要的在技术上是不可能的。原因如下:

这是一种特殊的压缩情况。您有很长的数据序列,您希望能够从较小的信息中无损地重新创建这些数据。如果您的要求是可能的,那么我将能够将整个堆栈溢出压缩为一个整数(因为整个网站可以序列化为一系列数字,尽管很长!)

不幸的是,数学不是这样工作的。任何给定的序列都有一个特定的熵度量——该序列的平均复杂度。为了无损地重现该序列,您必须能够编码至少足够的信息来表示其熵。

对于某些序列,实际上可能有一个种子能够生成一个长的、特定的序列,但这仅仅是因为有一个硬编码的数学函数采用该种子并产生一个特定的数字序列。但是,要获取 任意 值序列并生成这样的种子,您需要一个种子和一个能够从该种子生成该序列的函数。为了对这两个东西进行编码,您会发现您获得的数据比您预期的要多得多!

【讨论】:

  • 这是完全不正确的。 CSPRNGs 和 PRNGs 之间是有区别的,而 CSPRNGs 正是为了避免这种困境而设计的。一旦知道了 RNG 的初始种子(在 RNG 周期的数量级上——或者对于设计不佳的算法来说更少),序列中的所有数字都是确定性的。 en.wikipedia.org/wiki/Random_number_generator_attack
  • @ingyhere,我想您可能还没有阅读完整的原始问题。作者不是在问他是否可以从一个序列中生成数字,如果他知道密钥,他是在问是否有办法(不列举每个可能的选项)来识别单个数字密钥可以无损地产生任何随机数字序列.
【解决方案4】:

当然可以恢复 java.util.Random 使用的种子。 This post 描述了 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 开头的伪随机数字序列。

【讨论】:

    【解决方案5】:

    是的,对设计不佳的伪随机数生成器的数字流进行逆向工程绝对容易,例如 Java 编程语言中的线性同余 PRNG 实现 (java.util.Random)。

    事实上,只有来自特定生成器的 两个 值,以及有关值出现顺序的信息,就可以预测整个流。

    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;
        }
    }
    

    这正是使用经过社区广泛审查的加密安全随机数生成器对于需要安全性的实施至关重要的原因。

    还有更多关于逆向工程 PRNG 的信息,包括 java.util.Random here。 ...

    【讨论】:

    • PS,这不应该用作架构特性。我只是把它贴在这里以便大家知道。
    猜你喜欢
    • 2017-04-08
    • 2015-08-19
    • 1970-01-01
    • 2019-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-12
    • 1970-01-01
    相关资源
    最近更新 更多