19

I'm a little bit confused, how to do this. I know I can use Random class to generate random numbers, but I don't know how to specify and generate 8-byte number?

Thanks, Vuk

Vuk
  • 283
  • 2
  • 3
  • 5

5 Answers5

16

I agree with @aioobe' point about Random using a 48-bit seed. SecureRandom is a better solution. However to answer the OP's questions of how to use the Random class and still allow for all possible 8-byte values is to reset the seed periodically.

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 only allows a sequence of 2^47 long values. By using two Random generators, one which keeps jumping around in the sequence, you get two 2^47 * 2^47 possible values. The use of << 1 is to avoid the impact of having both randoms having the same seed (in which case ^ would produce 0 for 4 billion values in a row)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • +1, Great answer. Interesting notes regarding the reset and the shift-left! I would like the answer even better if you encapsulated the Random instance in an annonymous subclass of Random. – aioobe Dec 25 '10 at 22:57
  • Won't the two Random objects create exactly the same random numbers as they will both have the same seed? (i.e. on a fast computer System.currentTimeMillis() will be the same for both?) – Jaco Van Niekerk Jul 21 '12 at 08:26
  • On old versions of Java that is true. From Java 5.0, Random uses System.nanoTime() and a AtomicLong counter. – Peter Lawrey Jul 21 '12 at 09:40
15

You should note that the java.util.Random class uses a 48-bit seed, so not all 8-byte values (sequences of 64 bits) can be generated using this class. Due to this restriction I suggest you use SecureRandom and the nextBytes method in this situation.

The usage is quite similar to the java.util.Random solution.

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

Here is the reason why a 48-bit seed is not enough:

  • The Random class implements a pseudo random generator which means that it is deterministic.
  • The current "state" of the Random determines the future sequence of bits.
  • Since it has 248 states, it can't have more than 248 possible future sequences.
  • Since an 8-byte value has 264 different possibilities, some of these possibilities will never be read from the Random object.

Based on @Peter Lawreys excellent answer (it deserves more upvotes!): Here is a solution for creating a java.util.Random with 2×48-bit seed. That is, a java.util.Random instance capable of generating all possible 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;
    }
}
Community
  • 1
  • 1
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • it's not cool to leave the same comment under every single answer. – Roman Dec 25 '10 at 22:21
  • 2
    Why not? I think it's okay. It applies to each answer I commented on. – aioobe Dec 25 '10 at 22:22
  • My intuitive solution would have been to generate two 4-Byte values. If I understand you correctly this wouldn't work because the two values would be excluded (or at least not likely) from being equal when using the same Generator twice. E.g. FF FF would not be as likely as FF AA? I don't know how PRGs are implemented so this surprised me as I would expect each number to be (pseudo-)independent of previous numbers. – zckman Dec 26 '10 at 02:49
  • No, `FF FF` is just as likely as `FF AA`. It *should* be independent, but since it is pseudo-random it is not *completely* independent. That is, given the past 7 bytes in the sequence, the 8th byte can't take all possible values. The next byte may be independent from its previous byte, but not from its past 7 bytes. – aioobe Dec 26 '10 at 08:51
  • The only problem is that SecureRandom takes arbitrarily long time to generate the next random (it depends on the amount of entropy available on the OS), so it might not be the best option when you just want to generate a number within the entire range of long. – andresp Jan 30 '15 at 11:55
  • **A race condition** happened with one of my colleges most probably because of `if (count++ == 0) extra48bits = new ExposedRandom();` it causes the `extra48bits ` to be null and the next line `return super.next(bits) ^ extra48bits.next(bits) << 1;` throws null pointer exception for `extra48bits` is null. I suggest having the `extra48bits` to be set with a new ExposedRandom() on declaration to avoid such case. – Muhammad Hewedy Feb 02 '21 at 13:29
8

It can be done either with byte array of length 8:

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

or with a variable of type long (which represents 8-byte numbers):

long randomLong = random.nextLong();
Roman
  • 64,384
  • 92
  • 238
  • 332
  • 2
    Note that neither of these two alternatives is capable of generating all possible 8-byte values. See my answer. – aioobe Dec 25 '10 at 22:02
  • @aioobe: can you explain why? I've read the docs, and I've read the implementation and I don't have clear understanding still. There's rather complex (math-based) algorithm, and as I understand, it generates dependent values. And if it's true, then one random instance doesn't really generate all possible values of longs. But different `Random`s (with different "start points") do. Am I right? – Roman Dec 25 '10 at 22:26
  • 1
    A 48-bit seed has only 2^48 possible values and must repeat after 2^48 values at best. The underlying function produces a 32-bit int so you need two of these so you could end up with a sequence which repeats after 2^47 long values. If you reset the Random seed periodically you will just end up checking the point in the sequence but not the possible values. – Peter Lawrey Dec 25 '10 at 22:32
  • 1
    @Peter Lawrey, aioobe: thanks, now it's clear. But still, I would go with usual `Random` class (or with 2 random instances despite not quite uniformal distribution) until it's really necessary to generate fair values from the 2^64 range. Just because it's well-known and often-used class. – Roman Dec 25 '10 at 22:47
  • I agree. There is a point in not involving too many packages without strong reasons. [Here is a solution](http://ideone.com/j0eOm) for creating a `java.util.Random` instance with 96 bit seed instead. (Based on Peter Lawreys answer.) – aioobe Dec 25 '10 at 23:19
3

The long type is an 8 byte signed integer, so Random.nextLong() seems to do what you want. Or if you need a byte array as result:

byte[] result = new byte[8];
Random.nextBytes(result);
Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
1

A little adjusting from the code 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);
  }
}

Some further reading: Math.random() versus Random.nextInt(int)

Community
  • 1
  • 1
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135