views:

127

answers:

3

I'm looking to randomize a BigInteger. The intent is to pick a number from 1 to 8180385048. Though, from what I noticed, the BigInteger(BitLen, Random) does it from n to X2-1, I'd want some unpredictable number. I tried to make a method that would do it, but I keep running into bugs and have finally given in to asking on here. :P Does anyone have any suggestions on how to do this?

+1  A: 

Make a loop and get random BigIntegers of the minimum bit length that covers your range until you obtain one number in range. That should preserve the distribution of random numbers.

gpeche
Which is his specific case would use bitLen == 33.
GregS
+4  A: 

Judging from the docs of Random.nextInt(int n) which obviously needs to solve the same problem, they seem to have concluded that you can't do better than "resampling if out of range", but that the penalty is expected to be negligible.

From the docs:

The algorithm is slightly tricky. It rejects values that would result in an uneven distribution (due to the fact that 231 is not divisible by n). The probability of a value being rejected depends on n. The worst case is n=230+1, for which the probability of a reject is 1/2, and the expected number of iterations before the loop terminates is 2.

I'd suggest you simply use the randomizing constructor you mentioned and iterate until you reach a value that is in range, for instance like this:

public static BigInteger rndBigInt(BigInteger max) {
    Random rnd = new Random();
    do {
        BigInteger i = new BigInteger(max.bitLength(), rnd);
        if (i.compareTo(max) <= 0)
            return i;
    } while (true);
}

public static void main(String... args) {
    System.out.println(rndBigInt(new BigInteger("8180385048")));
}

For your particular case (with max = 8180385048), the probability of having to reiterate, even once, is about 4.8 %, so no worries :-)

aioobe
Thanks. Though, it would be "i.compareTo(max) <= 0" because I'm looking for a number equal or below the max. Otherwise, this is a solid example.
Unrealomega
Ah, right, updated. You might also want to take into account that 0 is a possible, return value... as you said from 1 to 8180385048
aioobe
A: 

Reiterating if out of range, as suggested in other answers, is a solution to this problem. However if you want to avoid this, another option is to use the modulus operator:

BigInteger i = new BigInteger(max.bitLength(), rnd);
i = i.mod(max);                 // Now 0 <= i <= max - 1
i = i.add(BigInteger.ONE);      // Now 1 <= i <= max
Grodriguez
This will most likely result in an uneven distribution.
Skip Head
Right, this is likely to introduce a bias towards smaller values. This might or might not be relevant for the original poster's application.
Grodriguez