字符串到唯一整数散列

Posted

技术标签:

【中文标题】字符串到唯一整数散列【英文标题】:String to unique integer hashing 【发布时间】:2013-05-07 10:24:23 【问题描述】:

我正在尝试开发一个可以将我的字符串更改为唯一整数值​​的系统,这意味着例如单词“account”的加密数值为 0891,并且没有其他单词可以转换为 0891相同的转换过程,它确实但是需要能够将生成的整数转换回字符串。

同时会依赖于词的结构规则,意思是“准确”、“公告”等词的生成数大于0891,“a”、“算盘”、“缩写”等词" 将生成小于 0891 的数字。

此应用程序的用途类似于索引或主键。我不使用增量索引的原因是出于安全目的,并且是由于索引依赖于集合中的数据数量

(例如)

[0] A, [1] B, [2] C, [3] D, [4] E, [5] F

上面的字母都有对应的索引,E的索引是4

但是如果数据突然增加或减少然后排序

[0] A, [1] AA, [2] AAB, [3] C, [4] D, [5] DA, [6] DZ, [7] E, [8] F

E 现在的索引为 7

每个单词必须有一个唯一的独立整数等价物,并具有相应的权重。

我需要知道是否存在可以执行上述操作的算法。

任何帮助将不胜感激。

【问题讨论】:

除非您规定最大字长,否则这是不可能的。 (即使您确实规定了字长,我仍然不确定)。 我会开始说,如果你想要安全,你应该放弃对“词结构规则”的依赖。您已经为具有此类要求的攻击者简化了工作。 UmNyobe 所说的,加上你应该接受碰撞。索引通常会有冲突,只要它们是例外而不是规则,这没有什么问题。 请注意,如果您想将某物转换为其他物,并且没有返回原始值的选项,您指的是散列而不是加密。您的问题是,大多数散列算法都有可能为不同的输入返回相同的散列。尤其是如果输出必须是整数值;这很有可能。 @marcolopes:只有 2^32 个可能的整数哈希码。有超过 2^32 个可能的字符串,因此String.hashCode() 保证为多个字符串生成相同的哈希码。 【参考方案1】:

这在您给出的约束条件下是不可能的,除非您施加最大长度。

假设k("a")k("b")是这两个字符串的编码。

根据您的限制,您正在寻找一个介于这两个值之间的唯一整数,但 k("a") < k("a....a") < k("b")。由于有无限数量的样式字符串"a....a"(和"akjhdsfkjhs")需要放在两个代码之间,因此不能存在这样的顺序保留通用、唯一、固定长度的代码对于任意长度的字符串。因为您需要与字符串一样多的整数,并且由于字符串不受长度限制,所以这是行不通的。

删除一般(因此不允许插入新字符串)、唯一(允许冲突 - 例如使用前四个字母作为代码!)、无限长度(例如 3 个字符)或保留顺序的属性。

【讨论】:

【参考方案2】:

为简单起见,我假设 az 是单词中唯一允许的字符。

让我们分配最多为 2 个字符串的数字:

String Value
a      0
aa     1
ab     2
...
az     26
b      27
ba     28
bb     29
...
bz     53
c      54
...

现在,通过查看它,您应该能够理解,要确定任何给定较短长度字符串的偏移量,您需要允许的最大长度。假设我们知道这个数字。

为了算法简单,我们更喜欢从 27 开始:(请随意尝试从 0 开始,您需要一些特殊情况)

String Value
a      27
aa     28
ab     29
...

因此,从本质上讲,最左边的字符贡献了一个值 27*(1-26)(对于 az),而右边的下一个字符(如果存在)将 1-26(对于 az)贡献给一个字符串的值。

现在这可以概括为最左边的数字会贡献(1-26)*27^(len-1),下一个是(1-26)*27^(len-2),依此类推,直到(1-26)*27^0

这让我找到了一些 Java 代码:

long result = 0;
for (int i = 0; i < s.length(); i++)
   result += pow(27, MAX_LENGTH - i - 1)*(1 + s.charAt(i) - 'a');

测试输出:

a                    =   150094635296999121
aa                   =   155653695863554644
aaa                  =   155859586995649293
aaaa                 =   155867212593134280
aaaaa                =   155867495022670761
abacus               =   161447654121636735
abbreviation         =   161763445236432690
account              =   167509959568845165
accuracy             =   167554723653128367
announcement         =   230924421746611173
z                    =  3902460517721977146

Online demo.

是的,对于最多 13 个长度的字符串,这些数字相当大,但是,如果不按顺序为实际字典中的单词分配数字,你就不能做得更好(除了你可以从 0 开始,即, 相对来说差别很小),因为字母序列的可能性有很多。

【讨论】:

【参考方案3】:

为了唯一性,从为字母分配质数开始: A -&gt; 2, B -&gt; 3, C -&gt; 5, D -&gt; 7

要计算单词中给定字母的“键”,请将素数提高到单词中位置索引的幂。要获得整个单词的“键”,请将所有字母键相乘。

例如单词CAB:

C -> 5 ^ 1 = 5
A -> 2 ^ 2 = 4
B -> 3 ^ 3 = 81
CAB -> 5 * 4 * 81 =  1620.

没有其他词可以给你 1620 作为钥匙。

注意:您不必以 A -> 2 开头或按顺序为字母表中的字符分配质数,只要您跟踪映射即可。还要记住,这样做的结果会很快变大。

但是,请记住其他有关安全性的 cmets - 这不是一个特别安全的算法。

【讨论】:

与 rahulroc 相同的答案,相同的反例:hash('abba') == hash("baab") 它真的按要求保留订单吗? AFAICT 您的订单不正确:B fwiw,在 python 2.7 中 hash('abba') 和 hash('baab') 不相等。我想知道他们在做什么【参考方案4】:

如果您对这些整数可以占用的字节数没有任何限制,那么每个字符的底层(例如 Ascii)字节码将为您提供整数表示。等效地,分配 0=A, 1=B 直到 Z=25,然后单词本身就是以 26 为底的整数。

【讨论】:

这将如何处理字符串“10020”和“100C0”?【参考方案5】:

按递增顺序为每个字母分配一个唯一的素数(顺序不需要)。

请注意:由于素数的乘法是一个唯一的结果,只能乘以这些数字,它会给你每个单词的唯一值。

算法:

int hash = 0;
forEach (int i = 0 ; i < word.length ; i++)
 
   hash *= (prime[c[i]] ** (length - i)); 

prime - 一个数组,用于存储对应于每个素数的值

赋予 (length - 1) 以赋予该字符出现的位置以保持字典顺序的值。

该算法将提供足够大的值,从而超出您的数组。

另外:长度较小的单词可能会比一些长度较大的单词给出更低的值,并且可能会影响您的字典顺序但我不确定您为什么需要字典顺序,因为唯一性会保持在这里。

【讨论】:

反例:hash('abba') == hash("baab") 另外,它不能扩展。你很快就用完了整数,而且它不会保持顺序:hash('b') &lt; hash('ab'),不是吗? @ErichSchubert 附带说明一下,有无限多的整数,尽管编程语言中的整数类型通常是有限的。跳过多少个整数可能是个好问题。 任意两个整数的中间并不是无限多的。但是ab 之间有无数个字符串。因此,除非您将无穷大分配给b,否则您将失败。另外,我敢打赌,问题作者的意思是有 finite 整数哈希码。 我已经在我的回答中提到,它很可能会为非常大的字符串提供非常大的数字。此外,长度较小的单词可能比长度较大的单词具有更少的哈希码。但是哈希('abba')!=哈希('baab')。假设a = 2,b = 3。然后 hash("abba")=23 * 32 * 31 *2 = 432 和 hash('baab')=32 * 2**2 * 2*1 * 3 = 648。这是不同的数字。素数相乘将得到唯一值,这些值只能通过这些素数获得。【参考方案6】:

是的,但大部分都不是。

是的,就像随机的答案一样。通过设置基数 26(或所有 ASCII 基数 128),理论上您可以唯一地散列每个字符串。

另一方面,这是不切实际的,不仅对于大多数语言来说数字会变得太大,而且这可能是一个非常耗时的过程。此外,如果允许字符串是无限的,那么Cantor's diagonal argument 的形式也可以应用“破坏”这个算法。不可能创建一个具有基数 aleph-one(字符串)的集合到一组基数 aleph-null(整数)的一对一映射。

【讨论】:

【参考方案7】:

你可以这样做:

SEPARETOR = '000'
string_to_hash = "some_string"
hashed_result = int(SEPARETOR.join(list(str(ord(character)) for character in string_to_hash)))

享受吧!

【讨论】:

【参考方案8】:

长度为n 的字符串s 的一般形式的函数是:

hashCode(s) = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

其中^ 表示取幂。由于 Java 使用 32 位整数来保存哈希值,因此所有值都应保持原样。

如果要将字符串散列为小整数,可以使用以下C# 代码:

int StringToIntegerHash(string str)

  int hash = 0;
  str = GetTicketHash(str);
  for(int i=0; i<str.Length;i++)
  
     hash +=(int) ((int)str[i]) * Math.Pow(2, str.Length - i);
  
  return hash;






string GetTicketHash(string str)

   const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   byte[] bytes = Encoding.UTF8.GetBytes(str);

   SHA256Managed hashstring = new SHA256Managed();
   byte[] hash = hashstring.ComputeHash(bytes);

   char[] hash2 = new char[16];

   // Note that here we are wasting bits of hash! 
   // But it isn't really important, because hash.Length == 32
   for (int i = 0; i < hash2.Length; i++)
   
     hash2[i] = chars[hash[i] % chars.Length];
   

   return new string(hash2);
 

【讨论】:

以上是关于字符串到唯一整数散列的主要内容,如果未能解决你的问题,请参考以下文章

具有 32 位整数的低冲突率的快速字符串散列算法 [关闭]

字符串

Rails 无法正确解码来自 jQuery 的 JSON(数组变成带有整数键的散列)

将整个PHP数组散列成唯一值[重复]

如何在python3.6中将字符串转换为唯一的整数,反之亦然(在0到255之间)

Django生成唯一字符串UUID与MD5