【问题标题】:Is it possible to compare two strings by their "hash" numbers?是否可以通过它们的“哈希”数字比较两个字符串?
【发布时间】:2011-03-29 16:07:22
【问题描述】:

我有一个永远丢失的字符串。我唯一拥有的就是一些神奇的哈希数。现在我有一个新字符串,它可能与丢失的字符串相似或相等。我需要知道它有多近。

Integer savedHash = 352736;
String newText = "this is new string";
if (Math.abs(hash(newText) - savedHash) < 100) {
  // wow, they are very close!
}

有没有为此目的的算法?

ps。文字长度不固定。

pps。我知道通常的哈希码是如何工作的。我对一种能够以不同方式工作的算法感兴趣,它可以为我提供上述功能。

pps。在一个非常简单的场景中,这个hash() 方法看起来像:

public int hash(String txt) {
  return txt.length();
}

【问题讨论】:

  • 为“哇,他们非常接近!”竖起大拇指
  • has 码的目的是提供良好的分布,这意味着非常接近(应该)的字符串的哈希码往往会有很大的不同。
  • 打开你的思路,伙计们:) 忘记 hashCode(),请再读一遍问题。
  • 哈希码有什么限制?它必须小于一定长度吗?否则,字符串本身的“哈希码”会起作用。另外,您对“非常接近”的定义是什么?小的插入和删除是否意味着它仍然接近(因为大部分内容被移动)或完全不同(因为每个索引处的所有字符都不同)?
  • @0x69 我没有破解任何东西,我需要开发算法,它会生成哈希数。

标签: java algorithm design-patterns


【解决方案1】:

在这种情况下,标准哈希将不起作用,因为关闭哈希值并不意味着关闭字符串。事实上,大多数散列函数旨在为相近的字符串提供非常不同的值,以便为任何给定的输入字符串集创建散列值的随机分布。

如果您可以访问这两个字符串,那么您可以使用某种字符串距离函数,例如Levenshtein distance。这会计算两个字符串之间的编辑距离,或将一个字符串转换为另一个字符串所需的编辑次数。

然而,在这种情况下,最好的方法可能是使用某种fuzzy hashing 技术。这样您就不必存储原始字符串,并且仍然可以获得某种相似性度量。

【讨论】:

  • 那么 OP 就不走运了,除非他设法通过找到一个与原始值完全匹配的字符串来恢复原始字符串(很有可能)。
  • +1 根据讨论,我怀疑这个问题更多的是关于编辑距离而不是散列。
  • "levenshtein" 需要两个字符串,而我只有一个(见问题)。
  • Fuzzy Hashing 是答案,非常感谢您的提示!如果您可以编辑答案以删除不相关的文本,那就太好了。再次感谢。
  • 谢谢,@yegor256。我编辑了答案以描述不同的方法,以及为什么模糊散列在这里起作用。
【解决方案2】:

如果哈希不匹配,则字符串不同。

如果哈希匹配,则字符串可能相同。

您无法从哈希值中推断出任何其他信息。

【讨论】:

    【解决方案3】:

    不,这行不通。哈希的相似性与原始字符串的相似性无关。事实上,完全有可能 2 个不同的字符串具有相同的哈希值。您可以肯定地说,如果哈希不同,则字符串不同。

    [根据评论编辑,碰撞的可能性当然是非常真实的]

    编辑澄清:

    如果您只有旧字符串的哈希值,那么您将无法找到该字符串的原始值。没有算法可以告诉您 2 个不同字符串的哈希值是否表示接近的字符串,即使存在也无济于事。即使您找到一个与您的旧字符串具有 exact 哈希匹配的字符串,您仍然无法知道它是否是您的原始字符串,因为任何数量的字符串都可以产生相同的哈希值。事实上,有大量* 字符串可以产生相同的哈希值。

    [理论上这个庞大的数字实际上是无限的,但在任何真实的存储系统上,您都无法生成无限多的字符串。在任何情况下,您通过这种方法匹配未知字符串的机会都非常小,除非您的哈希值相对于输入字符串很大,即使这样您也需要暴力破解每个可能的字符串]

    【讨论】:

    • 是的,通常的hashCode() 在这里不起作用。我正在寻找其他算法......
    • 这种可能性不仅仅是理论上的,而是 +1 :-)。
    • 嗯,没错。这取决于哈希算法,但对于现实世界的问题,您希望哈希冲突不太可能发生。
    • @yegor256 - 除了尝试 every 字符串并寻找匹配的哈希之外,恐怕没有任何方法可以从哈希到原始字符串,并且即使那样你也不确定。
    • @Steve,感谢您的回答,但超出了范围。我对常规哈希算法不感兴趣。我有兴趣找到一种新算法。你知道吗?
    【解决方案4】:

    正如其他人指出的那样,使用典型的哈希算法,它根本不会那样工作。

    不过,也有少数人制定了至少与此类似的算法。例如,有一家名为“Xpriori”的公司有一些散列(或最不类似于散列)算法允许这样的事情。他们会让你比较相似度,或者(例如)让你组合散列所以hash(a) + hash(b) == hash(a+b)(对于+的一些定义,不仅仅是简单的数字相加)。与大多数哈希一样,总是存在冲突的可能性,因此您有一些误报的机会(但通过选择哈希大小,您可以将该机会设置为任意小的值)。

    因此,如果您要处理现有数据,那么您可能就不走运了。如果您正在创建新的东西,并且想要按此顺序提供功能,这是可能的——尽管尝试自己做认真并非易事。

    【讨论】:

    【解决方案5】:

    没有。哈希的设计使得输入字符串的微小变化会导致结果哈希的巨大差异。这对于字典实现以及验证文件的完整性非常有用(单个更改的位将导致完全不同的哈希)。所以不,这不是你可以用来做不等式比较的东西。

    【讨论】:

      【解决方案6】:

      如果 hashCode 不同,则不能是相同的 String,但是许多 String 可以具有相同的 hashCode()。

      根据字符串的性质,进行简单比较可能比比较 hashCode() 更有效,它必须检查每个字符并执行计算,而比较可以提前存储,例如如果长度不同或一旦看到不同的字符。

      【讨论】:

      • 问题与 hashCode() 无关,请再次查看更新后的问题
      • 散列算法的重点是最大化比特数,这些比特数会随着源信息中单个比特的不同而变化。即理想情况下,“A”和“B”会产生一个哈希码,其中一半相同,一半不同。即映射实际上是随机的。接近度的概念仅在确定您是否具有较差的哈希算法时有用。这是您要确定的吗?一个简单的方法是^ xor 并计算所设置的数字(即不同),16(一半)是最佳的。
      【解决方案7】:

      根据定义,任何好的散列算法都不会为相似的参数产生相似的散列。不然太容易破解了。如果“aaaa”的散列值看起来类似于“aaab”,那么这是一个糟糕的散列。我以前也遇到过这样的问题,没有太大的困难(有趣的谜题要解决!)但你永远不知道你的哈希算法可能很差。知道它是什么?

      如果您有时间,您可以通过散列每个可能的单词来强制使用此解决方案。不优雅,但可能。如果您也知道原始单词的长度,则更容易。

      如果是标准有算法,比如MD5,你可以找到已经有大量source和hash映射的网站,然后通过这种方式得到答案。试试http://hashcrack.com/

      在我们的一位开发人员离开后,我成功使用了这个网站,我需要找回密码。

      干杯,

      丹尼尔

      【讨论】:

        【解决方案8】:

        您可以将字符串视为一个非常大的数字,但这与您在一般情况下的能力范围有关。如果你有一个特定的问题域,你可以将字符串的表示压缩成更小的东西而不会造成损失,但它仍然不是很有用。

        例如,如果您正在处理单个单词,您可以使用soundex 来比较两个单词的发音相似度...

        使用传统哈希码可以做的最好的事情是比较两个字符串的相等性和可能的​​不等式。假阳性是可能的,但不会有假阴性。但是,您无法通过这种方式比较相似度。

        【讨论】:

        • soundex 不起作用,因为我没有第一个字符串。我唯一拥有的是它的“哈希码”。
        【解决方案9】:

        当对象发生一点变化时,正常的哈希码会发生很大变化。这是为了区分不同的物体,而不在乎它们有多相似。因此答案是否定的

        【讨论】:

          【解决方案10】:

          好吧,看来你想要的不是真正的字符串散列,而是一些字符串的指纹。因为您希望它是 32 位的,所以一种方法可能是:

          在字符串的前半部分和后半部分之间计算Pearson correlation coefficient(如果字符串长度是奇数个字符,则添加一些填充)并将此数字存储为 32 位浮点数。但我不确定这种方法有多可靠。

          ==编辑==
          这是实现这个想法的 C 示例代码(未优化)(稍作修改):

          #include <stdio.h>
          #include <stdlib.h>
          #include <math.h>
          #include <string.h>
          
          float mean(char *str) {
            char *x;
            float sum = 0.0;
          
            for(x=str; *x!='\0'; x++) {
              sum += (float) *x;
            }
            return sum/strlen(str);
          }
          
          float stddev(char *str) {
            char *x;
            float sum = 0.0;
            float u = mean(str);
          
            for(x=str; *x!='\0'; x++) {
              sum += ((float)*x - u)*((float)*x - u);
            }
            return sqrt(sum/strlen(str));
          }
          
          float covariance(char *str1, char *str2) {
            int i;
            int im = fmin(strlen(str1),strlen(str2));
            float sum = 0.0;
            float u1 = mean(str1);
            float u2 = mean(str2);
          
            for(i=0; i<im; i++) {
              sum += ((float)str1[i] - u1)*((float)str2[i] - u2);
            }
            return sum/im;
          }
          
          float correlation(char *str1, char *str2) {
            float cov = covariance(str1,str2);
            float dev1 = stddev(str1);
            float dev2 = stddev(str2);
            return cov/(dev1*dev2);
          }
          
          float string_fingerprint(char *str) {
            int len = strlen(str);
            char *rot = (char*) malloc((len+1)*sizeof(char));
            int i;
            // rotate string by CHAR_COUNT/2
            for(i=0; i<len; i++){
              rot[i] = str[(i+len/2)%len];
            }
            rot[len] = '\0';
            // now calculate correlation between original and rotated strings
            float corr = correlation(str,rot);
            free(rot);
            return corr;
          }
          
          int main() {
            char string1[] = "The quick brown fox jumps over the lazy dog";
            char string2[] = "The slow brown fox jumps over the crazy dog";
            float f1 = string_fingerprint(string1);
            float f2 = string_fingerprint(string2);
            if (fabs(f1 - f2) < 0.2) {
              printf("wow, they are very close!\n");
            }
            return 0;
          }
          

          hth!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-27
            • 2023-03-16
            • 2017-05-23
            • 2019-08-15
            • 1970-01-01
            • 2015-03-07
            相关资源
            最近更新 更多