首页 > 解决方案 > 极其紧凑的 UUID(使用所有字母数字字符)

问题描述

我需要一个非常紧凑的 UUID,越短越好。

为此,我写道:

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

通过执行此代码,我得到,例如:

5luppaye6086d5wp4fqyz57xb

这很好,但不是最好的。Base 36 使用所有数字和小写字母,但不使用大写字母。

如果可以使用大写字母作为与小写字母分开的数字,则可以推测出由这些数字组成的数字基数 62:

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

我还可以使用重音字符(例如“è”或“é”)或特殊字符(例如“$”或“!”)对数字基数进行理论化,从而进一步增加可用的位数。

然而,使用这些重音或特殊字符可能会给我带来问题,所以目前我不想考虑它们。

在所有这些前提之后,如何将代表我的 UUID 的 BigInteger 转换为上述理论化的基数 62,以使其更加紧凑?谢谢

我已经验证了下面这样的代码是不可用的,因为超过 36 的每个基数都被视为基数 10:

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

毕竟,在数学中没有我想象的以 62 为基数,但我想在 Java 中可以创建它。

标签: javamath

解决方案


将数字转换为任何基数的通用算法基于除以余数。

您首先将数字除以基数。余数为您提供数字的最后一位 - 您将其映射到符号。如果商不为零,则将其除以基数。余数为您提供倒数第二个数字。然后你用商重复这个过程。

在 Java 中,使用 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();
}

你需要标识符是 UUID 吗?难道它不能只是任何随机的字母和数字序列吗?如果这是可以接受的,您就不必处理数基转换。

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();
}

推荐阅读