The problem of the substring method is, that it will fail unexpectedly.
Why? The javadoc of BigInteger(int numBits, Random rnd)
says
Constructs a randomly generated BigInteger, uniformly distributed over the range 0 to (2numBits - 1), inclusive.
Which means the BigInteger
could be one. The toString(32)
would generate the string 1
. From this string the substring(0, 12)
would fail with a StringIndexOutOfBoundsException
.
The requirement is to get a string representation of a randomly generated number in radix 32 with twelf digits
.
Whe can do some maths.
- a digit of base 32 can be represented with five bits (the value 0-31)
- for a number of base 32 with twelf digits we need as minimum
11*5 bit + 1
bits (100000000000 in radix 32)
- so we need to find a random number which is in the range
11*5 bit + 1
and 12 * 5 bit
The smallest twelf digit number in radix 32 as hex is 0x80000000000000
and the biggest is 0xfffffffffffffff
.
So the code for this computation would be (as verbose)
Random random = new Random();
// get a random value
long nextLong = random.nextLong();
// to ensure the value is in the expected range we
// limit the value to not exceed the maximum "& 0xfffffffffffffffL"
// set the highest bit "| 0x800000000000000L"
// convert the value to a string representation in radix 32
String value=Long.toString(nextLong & 0xfffffffffffffffL | 0x800000000000000L, 32);
For better understanding some visual explanation. ;-)
Long minTwelfDigitRadix32 = Long.valueOf("100000000000", 32);
Long maxTwelfDigitRadix32 = Long.valueOf("vvvvvvvvvvvv", 32);
System.out.printf("[%60s]%n", Long.toBinaryString(minTwelfDigitRadix32));
System.out.printf("[%60s]%n", Long.toBinaryString(maxTwelfDigitRadix32));
System.out.printf("[%15s]%n", Long.toHexString(minTwelfDigitRadix32));
System.out.printf("[%15s]%n", Long.toHexString(maxTwelfDigitRadix32));
output
[ 10000000000000000000000000000000000000000000000000000000]
[111111111111111111111111111111111111111111111111111111111111]
[ 80000000000000]
[fffffffffffffff]
PS: Setting always the highest bit will reduce the number of random values. If this is not desired we could first check if the
random number is greater or equal to the minimum number 100000000000
(radix 32) and set a random bit of the highest digit.
Using the java 8 Stream API a soulution would be
String value = IntStream.range(1, 13)
.map((int o) -> o == 1 ? random.nextInt(31) + 1 : random.nextInt(32))
.boxed()
.map(o -> Integer.toString(o, 32))
.collect(Collectors.joining());
- generate an ordered
int
stream with the values 1 to 13
- for each value in the stream we compute a random value, for the first position (the highest digit we need to ensure that it's value is in the range from 1 to 31, see above), for all other we compute a value in the range from 0 to 31)
- we need to convert the primitve
int
into Integers
- map each random value to a string representation using radix 32
- join all strings
PPS: If this is to be used for a huge number of values, the first way might perform better. Have not done some exhaustive test.