【问题标题】:Java hashcode reverse calculationJava hashcode逆向计算
【发布时间】:2013-12-27 22:29:12
【问题描述】:

我正在尝试通过添加一个新字符并反过来减去它来测试 String 哈希码的工作原理,但是当我进行计算时它没有给我正确的答案。

例如

int PRIME = 31;

//this will print 1687846330 
System.out.println("mlkjihgfedcb".hashCode());   

//this will print 783628775 which equals to "mlkjihgfedcba".hashCode();
System.out.println(("mlkjihgfedcb".hashCode() * PRIME + (int) 'a')); 

//this will print 25278344 which doesn't equals to "mlkjihgfedcb".hashCode()
System.out.println(("mlkjihgfedcba".hashCode() - (int) 'a') / PRIME); 

我想知道我在上面的最后一步中是否正确地进行了数学运算?溢出对于计算应该不重要吧?

【问题讨论】:

  • 这是不可逆的。例如stackoverflow.com/questions/9406775/…
  • 这在很多层面上都是错误的
  • @Bill 我没有看到人们解释为什么它在那个线程中不可逆......你在哪里看到它?
  • 如果两个不同的字符串可以产生相同的结果,您无法知道其中的哪一个字符串产生了该哈希码,因此您无法反转计算。

标签: java int hashcode string-hashing


【解决方案1】:

哈希函数不是可逆函数。 作为证明,考虑Pigeonhole principle,您只能将整数范围内的值存储在 hashCode 中,但字符串的数量是无限的。因此,必须有多个具有相同 hashCode 的字符串(并且有)。

【讨论】:

    【解决方案2】:

    一个大数乘以 31 会导致整数溢出,不能用除以 31 来反转。

    但请稍等:在 Java 中使用 int 的算术等价于模 232 的算术。由于 31 是奇数,它有一个 multiplicative inverse 模 232。因此,乘以 31 的乘法可以与乘以所述“逆”的乘法相反。那就是:

    int inverse = -1108378657;
    // This will print the hashCode of mlkjihgfedcb
    System.out.println(("mlkjihgfedcba".hashCode() - (int) 'a') * inverse); 
    

    【讨论】:

    • 我们如何得到或计算倒数?
    • 可以使用扩展欧几里得算法,也可以使用Hensel's lemma。后者最容易实现:先设置x = a = 31,然后重复x = x*(2-a*x),直到收敛,最多五次。
    • 但你不用计算,是-1108378657
    【解决方案3】:

    这个失败的具体原因是

    1687846330 * 31 + 97 = 52323236327
    

    数字 52,323,236,327 比 int 可以容纳的数字大得多,因此信息会从左侧丢失。该信息无法通过反转hashCode() 算法来恢复。

    如果您进行算术运算,您会发现第二个字符串的哈希码正好是 52323236327 mod 231

    更重要的是,从对象到哈希码的映射旨在不透明且不可逆。仅仅因为它今天以特定的方式实现并不意味着库的下一个版本必须以同样的方式进行。唯一的保证是碰撞的可能性极低(但非零)。

    【讨论】:

    • 我不知道在不破坏任何使用字符串参数的switch 语句的情况下,字符串的哈希函数的行为会以任何方式改变,因为编译后的代码包含字符串的硬编码哈希值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    相关资源
    最近更新 更多