【问题标题】:Generate 9 digit unique value from a String从字符串生成 9 位唯一值
【发布时间】:2017-10-31 10:24:09
【问题描述】:

我有员工数据,每个员工都有地址信息。我需要为邮政编码(5 个字符)和地址 line1(35 个字符)生成一个唯一的 9 位(数字或字母数字)值,这是表示位置的唯一值。它也被称为“Wrap number”。

如下图所示,当两个员工的地址相同时,Wrap Number 应该相同,否则应该分配新的值。

哪种算法最适合生成 9 位唯一值?

附:我需要用 Java 编程。

【问题讨论】:

  • 哈希怎么样?
  • 地址相同但邮政编码不同时会发生什么?此外,似乎可能有一些额外的业务逻辑,你应该得到一些澄清。因此,您可以使用用于存储记录的数据库分配的 id,可选的左侧填充为 '0'
  • 提示:您无法保证它是唯一的,这仅仅是因为可行的地址比 9 个字符的唯一代码要多。
  • 创建一个数据库表,主键由名称和地址组成。然后让数据库做维护一个自增列的工作。除此之外,Java 中的某种 UUID 技巧可能是你能做的最好的。
  • 这样做的真正目的是什么?您如何访问/索引这些数据?

标签: java algorithm unique uniqueidentifier


【解决方案1】:

你问的是不可能的。不,真的,不可能。

您有一个 5 位数的邮政编码,可以用 17 位编码。然后你有 35 个字符的文本。假设您将其限制为大写和小写字母,加上数字和特殊字符。图 96 个可能的字符,或每个大约 6.5 位。所以:

35 * 6.5 = 227.5 ~ 228 bits

因此,您有多达 245 位信息,并且您想创建一个“唯一”的 9 字符代码。您的 9 字符代码仅占用 72 位。您不能将 228 位信息打包成 72 位而不重复。见Pigeonhole principle

更好的解决方案是为每个员工分配一个序列号。如果您想制作那些 9 个字符的代码,请使用一种技术来混淆数字并使用 base-36(数字和大写字母)或类似的东西对其进行编码。我在我的博文How to generate unique "random-looking" keys 中解释了如何做到这一点。

【讨论】:

  • 地址的熵非常低,您的分析没有考虑到这一点。有超过 100 万亿个唯一代码可用,而世界上只有几十亿个地址可供分配,因此鸽巢原则并不适用。真正的压缩,可以利用地址的低熵,可能是一个解决方案。
  • @erickson:是的。地址不是完全随机的。尽管如此,减少问题空间并不能消除哈希冲突的可能性,只是稍微减少了它。例如,使用 64 位散列,碰撞概率达到 50%,生成 2^32 个代码(大约)。使用 2^34 个代码几乎可以确定。因此,即使世界上“只有”几十亿个地址,也很可能会产生重复。而且我们仍然存在将这些值编码为 9 个字符的标识符的问题。
【解决方案2】:

简单的想法是使用众所周知的哈希算法,这些算法已经在 J​​ava 中实现。

private static long generateIdentifier(final String adrLine, final String postCode) {
    final String resultInput = adrLine + postCode;

    //do not forget about charset you want to work with
    final byte[] inputBytes = resultInput.getBytes(Charset.defaultCharset());
    byte[] outputBytes = null;

    try {
        //feel free to choose the encoding base like MD5, SHA-1, SHA-256
        final MessageDigest digest = MessageDigest.getInstance("SHA-256");
        outputBytes = digest.digest(inputBytes);
    } catch (NoSuchAlgorithmException e) {
        //do whatever you want, better throw some exception with error message
    }

    long digitResult = -1;
    if (outputBytes != null) {
        digitResult = Long.parseLong(convertByteArrayToHexString(outputBytes).substring(0, 7), 16);
    }

    return digitResult;
}

//this method also may be useful for you if you decide to use the full result
// or you need the appropriate hex representation
private static String convertByteArrayToHexString(byte[] arrayBytes) {
    final StringBuilder stringBuffer = new StringBuilder();
    for (byte arrByte: arrayBytes) {
        stringBuffer.append(Integer.toString((arrByte & 0xff) + 0x100, 16)
                .substring(1));
    }
    return stringBuffer.toString();
}

我建议您不要使用 MD5 和 SHA1,因为这些哈希函数可以提供冲突。

【讨论】:

  • 你如何保证结果的长度=9?
  • 这不可能。他拥有大约 300 位的信息,而您正在生成一个 64 位的散列。通过 Pigeonhole 原理,我们知道多个键将散列到相同的值。你的算法也有碰撞问题。此外,将 64 位二进制值转换为十六进制将为您提供 16 字节的字符串。 OP要求9个字符。即使您使用 base-64 对其进行编码,该字符串也将是 12 个字符。
  • 好吧,我弄错了:它大约有 228 位信息。这仍然比您的 64 位大得多。
  • MD5 只有在有人故意更改消息时才会产生冲突。再次重申,SHA-1 冲突并非偶然发生,它们是故意制造的,而且处理时间更长。
【解决方案3】:

我的想法是这样的:

String str = addressLine + postalCode;
UUID uid = UUID.nameUUIDFromBytes(str.getBytes());
return makeItNineDigits(uid);

makeItNineDigits 是根据您的喜好对 UUID 字符串表示进行一些简化。 :) 这可能是uid.ToString().substring(0, 9)。或者您可以取两个长值 getLeastSignificantBitsgetMostSignificantBits 并从中创建一个 9 位值。

【讨论】:

  • 会导致碰撞
  • 你能说得更具体点吗? @vish4071
  • 默认 UUID 不是 9 个字符。因此,如果您将子字符串设为 9 个字符,则会导致冲突
  • 切断前 9 个字符并不是最好的解决方案。但很明显,9 位数字永远不足以避免所有可能的地址发生冲突。名称空间太大了。更重要的是 UUID 从输入数组中生成均衡的随机分布。 OP 可能会将此输出减少到所需的 9 位空间。无论如何,谢谢指出这一点。 @vish4071
  • UUID 是一个 128 位的结构。 OP 有大约 228 位要编码。即使使用了整个 UUID,也无法保证唯一性。
【解决方案4】:

一个简单的选择可能是利用 Java 内置的散列......

String generateIdentifier(String postCode, String addressLine) {
  long hash = ((postCode.hashCode() & 0xffffffffL) << 14L) 
          ^ (addressLine.hashCode() & 0xffffffffL); 
  return Long.toString(hash, 36);
}

【讨论】:

  • @Stefan Haustein,使用此解决方案,生成的结果为 15 位长度。如果按照我的要求将其修剪为 9 位,我可能会得到重复
  • 我已将班次调整为仅实际生成 9 位数字并测试了代码。它以前不应该产生超过 10 个或 11 个。错字?
  • @NaveenChappa:一个 9 个字符的字母数字(大写字母和数字)代码可以表示大约 10^14 个唯一项目。问题不在于表示长度,而是有可能为不相等的字符串生成重复的哈希码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
  • 2016-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多