1

I have been using this code to help me generate a random number of aplphabet and numbers. However, it generated 26 alpha,numerical number for me. How should i reduce it to 12 alpha numeric? Which is from "vc24maepo1g3at4ucsv4t9radm" to "553b4f6362f6c".

 public final class SessionIdentifierGenerator {
    private SecureRandom random = new SecureRandom();

    public String nextSessionId() {
        return new BigInteger(130, random).toString(32);
    }
}
Cheong Charlene
  • 275
  • 1
  • 2
  • 12

1 Answers1

1

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.

SubOptimal
  • 22,518
  • 3
  • 53
  • 69