javamath

Extremely compact UUID (using all alphanumeric characters)


I need an extremely compact UUID, the shorter the better.

To that end, I wrote:

    public String getBase36UIID() {
        // More compact version of UUID
        String strUUID = UUID.randomUUID().toString().replace("-", "");
        return new BigInteger(strUUID, 16).toString(36);
    }

By executing this code, I get, for example:

5luppaye6086d5wp4fqyz57xb

That's good, but it's not the best. Base 36 uses all numeric digits and lowercase letters, but does not use uppercase letters.

If it were possible to use uppercase letters as separate digits from lowercase letters, it would be possible to theorize a numerical base 62, composed of these digits:

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

I could theorize numerical bases also using accented characters such as "è" or "é", or special characters such as "$" or "!", further increasing the number of digits available.

The use of these accented or special characters, however, may cause me problems, so for the moment I prefer not to consider them.

After all these premises, how can I convert the BigInteger representing my UUID into the base 62 above theorized, in order to make it even more compact? Thanks

I have already verified that a code like the following is not usable, because every base over 36 is treated as base 10:

return new BigInteger(strUUID, 16).toString(62);

After all, in mathematics there is no base 62 as I imagined it, but I suppose that in Java it can be created.


Solution

  • The general algorithm for converting a number to any base is based on division with remainder.

    You start by dividing the number by the base. The remainder gives you the last digit of the number - you map it to a symbol. If the quotient is nonzero, you divide it by the base. The remainder gives you the second to last digit. And you repeat the process with the quotient.

    In Java, with BigInteger:

    String toBase62(BigInteger number) {
        String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        BigInteger base = BigInteger.valueOf(symbols.length());
    
        StringBuilder result = new StringBuilder();
        do {
            BigInteger[] quotientAndRemainder = number.divideAndRemainder(base);
            number = quotientAndRemainder[0];
            result.append(symbols.charAt(quotientAndRemainder[1].intValue()));
        } while (number.compareTo(BigInteger.ZERO) > 0);
        
        return result.reverse().toString();
    }
    

    Do you need the identifier to be a UUID though? Couldn't it be just any random sequence of letters and numbers? If that's acceptable, you don't have to deal with number base conversions.

    String randomString(int length) {
        String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        Random rnd = new Random();
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < length; i++) {
            str.append(symbols.charAt(rnd.nextInt(symbols.length())));
        }
        return str.toString();
    }