首页 > 解决方案 > 在 Swift 中生成字符串的自定义长度哈希值

问题描述

是否有可能以某种方式将长度为 n 的给定字符串“散列”为任意长度 m 的散列值?我想实现如下目标:

let s1 = "<UNIQUE_USER_IDENTIFIER_1>" 
let s2 = "<UNIQUE_USER_IDENTIFIER_2>"

let x1 = s1.hashValue(length: 4) 
let x2 = s2.hashValue(length: 4) 

我想根据其唯一的 UID 为每个给定用户分配一个(例如四位数)号码。那可能吗?

标签: iosswifthash

解决方案


首先,我想清楚你的意思是“散列”而不是“(无损)压缩”。您应该预料到一些冲突,其中 x1 和 x2 对于不同的 s1 和 s2 是相同的值。如果您真的是指映射以便没有冲突,那么我们必须更多地了解这个问题。在一般情况下是不可能实现的(参见鸽洞原理)。但是在一些输入有足够冗余的特殊情况下可以实现。或者可以通过维护一个表(即数据库等)来完成。这个答案的其余部分是关于散列的。

如果您的 UID 是在 iOS 上创建的 UUID(或任何 v4 UUID),那么它的位已经相当高质量,并且最后四位数字应该没问题,根本不需要进行任何散列。中间有几个字节是您应该避免的,但整个结尾部分是随机的,因此是理想的散列。

如果您的 UUID 不是随机的,您可以尝试使用默认散列并从中提取所需的位数,但非加密散列在它们的位之间并不总是具有良好的独立性,因此这可能会比您喜欢的更多。

在这种情况下,使用大于所需大小的加密哈希并将其截断(或取最低有效位;任何一组都可以)。这通常在密码学中完成。例如 SHA-512/256 是一种常用的散列,它计算 512 位散列并从中提取 256 位。加密散列要求其所有位高度独立,因此任何位子集也将是抗冲突的。

顺便说一句,如果您的意思是“4 个十进制数字”,那么您应该期望在 100 中发生大约 1 次冲突。如果您的意思是 16 位(4 个十六进制数字),您应该期望在 300 中发生大约一次冲突。这些是您最好的-案例场景并意味着您的哈希运行良好。有关期望表和一些有用的近似值,请参阅生日攻击


推荐阅读