【问题标题】:Generate 8-byte number in Java在 Java 中生成 8 字节数字
【发布时间】:2010-12-25 21:47:30
【问题描述】:

我有点困惑,如何做到这一点。我知道我可以使用 Random 类来生成随机数,但是我不知道如何指定和生成 8 字节数?

谢谢, 武克

【问题讨论】:

    标签: java


    【解决方案1】:

    您应该注意,java.util.Random 类使用 48 位种子,因此并非所有 8 字节值(64 位序列)都可以使用该类生成。由于这个限制,我建议你在这种情况下使用SecureRandom 和nextBytes method。

    用法与java.util.Random解决方案非常相似。

    SecureRandom sr = new SecureRandom();
    byte[] rndBytes = new byte[8];
    sr.nextBytes(rndBytes);
    

    以下是 48 位种子不够用的原因:

    • Random 类实现了一个伪随机生成器,这意味着它是确定性的。
    • Random 的当前“状态”决定了未来的位序列。
    • 由于它有 248 个状态,它不能有超过 248 个可能的未来序列。
    • 由于一个 8 字节的值有 264 种不同的可能性,其中一些可能性永远不会从 Random 对象中读取。

    基于@Peter Lawreys excellent answer(它值得更多的支持!):这是使用 2×48 位种子创建 java.util.Random 的解决方案。也就是说,java.util.Random 实例能够生成所有可能的longs。

    class Random96 extends Random {
        int count = 0;
        ExposedRandom extra48bits;
    
        class ExposedRandom extends Random {
            public int next(int bits) {    // Expose the next-method.
                return super.next(bits);
            }
        }
    
        @Override
        protected int next(int bits) {
            if (count++ == 0)
                extra48bits = new ExposedRandom();
            return super.next(bits) ^ extra48bits.next(bits) << 1;
        }
    }
    

    【讨论】:

    • 在每个答案下都留下相同的评论并不酷。
    • 为什么不呢?我觉得没关系。它适用于我评论的每个答案。
    • 我直观的解决方案是生成两个 4 字节的值。如果我理解正确,这是行不通的,因为当使用相同的生成器两次时,这两个值将被排除(或至少不可能)相等。例如。 FF FF 不会像 FF AA 那样可能吗?我不知道 PRG 是如何实现的,所以这让我感到惊讶,因为我希望每个数字都(伪)独立于以前的数字。
    • 不,FF FF 与 FF AA 一样可能。它应该是独立的,但由于它是伪随机的,它不是完全独立的。也就是说,给定序列中过去的 7 个字节,第 8 个字节不能取所有可能的值。下一个字节可能独立于其前一个字节,但不独立于其过去的 7 个字节。
    • 唯一的问题是 SecureRandom 需要任意长的时间来生成下一个随机数(这取决于操作系统上可用的熵量),因此当您只想在整个 long 范围内生成一个数字。
    【解决方案2】:

    我同意 @aioobe 关于 Random 使用 48 位种子的观点。 SecureRandom 是一个更好的解决方案。但是,要回答 OP 关于如何使用 Random 类并仍然允许所有可能的 8 字节值的问题,就是定期重置种子。

    int counter = 0;
    Random rand = new Random();
    Random rand2 = new Random();
    
    if (++counter == 0) rand = new Random(); // reset every 4 billion values.
    
    long randomLong = rand.nextLong() ^ rand2.nextLong() << 1;
    

    Random 只允许 2^47 长值的序列。通过使用两个随机生成器,一个在序列中不断跳动,你得到两个 2^47 * 2^47 的可能值。

    【讨论】:

    • +1,很好的答案。关于重置和左移的有趣注释!如果您将 Random 实例封装在 Random 的匿名子类中,我希望得到更好的答案。
    • 这两个 Random 对象不会创建完全相同的随机数,因为它们都将具有相同的种子吗? (即在快速计算机上 System.currentTimeMillis() 对两者都一样吗?)
    • 在旧版本的 Java 上确实如此。从 Java 5.0 开始,Random 使用 System.nanoTime() 和 AtomicLong 计数器。
    【解决方案3】:

    可以使用长度为 8 的字节数组来完成:

    byte[] byteArray = new byte[8];    
    random.nextBytes(byteArray);
    

    或使用long 类型的变量(表示8 字节数字):

    long randomLong = random.nextLong();
    

    【讨论】:

    • 请注意,这两种选择都不能生成所有可能的 8 字节值。看我的回答。
    • @aioobe:你能解释一下为什么吗?我已经阅读了文档,并且已经阅读了实现,但我仍然没有清楚的理解。有相当复杂的(基于数学的)算法,据我了解,它会生成依赖值。如果这是真的,那么一个随机实例并不会真正生成所有可能的 long 值。但是不同的Randoms(具有不同的“起点”)可以。我说的对吗?
    • 48 位种子只有 2^48 个可能的值,并且最多必须在 2^48 个值之后重复。底层函数生成一个 32 位 int,因此您需要其中两个,这样您就可以得到一个在 2^47 个长值之后重复的序列。如果您定期重置随机种子,您最终只会检查序列中的点,而不是可能的值。
    • @Peter Lawrey,aioobe:谢谢,现在很清楚了。但是,我仍然会使用通常的 Random 类(或使用 2 个随机实例,尽管分布不是很均匀),直到确实有必要从 2^64 范围内生成公平值。只是因为它是众所周知且经常使用的类。
    • 我同意。在没有充分理由的情况下不要涉及太多包是有道理的。 Here is a solution 用于创建具有 96 位种子的 java.util.Random 实例。 (基于 Peter Lawreys 的回答。)
    【解决方案4】:

    long 类型是一个 8 字节有符号整数,所以 Random.nextLong() 似乎可以满足您的需求。或者,如果您需要一个字节数组作为结果:

    byte[] result = new byte[8];
    Random.nextBytes(result);
    

    【讨论】:

    • 请注意,Random 类无法生成所有可能的 long。
    【解决方案5】:

    对代码here稍作调整:

    import java.util.Random;
    
    /** Generate 10 random integers in the range 0..99. */
    public final class RandomByte {
    
      public static final void main(String... aArgs){
        log("Generating 10 random integers in range 0..255.");
    
        //note a single Random object is reused here
        Random randomGenerator = new Random();
        for (int idx = 1; idx <= 10; ++idx){
          int randomInt = randomGenerator.nextInt(256);
          // int randomInt = randomGenerator.nextBytes(256);
          log("Generated : " + randomInt);
        }
    
        log("Done.");
      }
    
      private static void log(String aMessage){
        System.out.println(aMessage);
      }
    }
    

    进一步阅读:Math.random() versus Random.nextInt(int)

    【讨论】:

    • 请注意,Random 类无法生成所有可能的 8 字节值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-28
    • 2015-09-01
    相关资源
    最近更新 更多