【问题标题】: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() 保证为多个字符串生成相同的哈希码。

标签: algorithm search hash


【解决方案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);
           }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-11-01
            • 1970-01-01
            • 1970-01-01
            • 2015-10-20
            • 2018-05-12
            • 1970-01-01
            • 2013-04-11
            • 1970-01-01
            相关资源
            最近更新 更多