【问题标题】:Reverting an hash code "sum"恢复哈希码“sum”
【发布时间】:2017-11-04 09:22:04
【问题描述】:

所以,与其每次都计算哈希码,我想我可以保持一个整数更新所有的变化。想象一个用于生成哈希码的属性的设置器:

public void setFoo(Foo newFoo){
    this.hashCalculator.remove(this.foo.hashCode()); // remove old hash code
    this.hashCalculator.add(newFoo.hashCode()); // add new hash code
    this.foo = newFoo; // set new foo
}

(我希望我没有做一些愚蠢的事情)我认为这将是简单的数学运算,但我未能实现它。我认为这与整数溢出有关,但我不确定。显然我错过了一些东西。除法的余数可能应该加回该值,对吧?

这是我的代码。最后结果应该是 1,但这不是我得到的。

class HashCode
{
    public int value = 1;

    public void add(int val){
        // as suggested by effective java
        value = value * 37 + val;
    }

    public void remove(int val){
        value = (value - val) / 37;
    }
}

HashCode o = new HashCode();

for(int a = 0; a < 1000; a++){
    o.add(a);
}

for(int r = 0; r < 1000; r++){
    o.remove(r);
}

System.out.println(o.value); // should be 1

【问题讨论】:

  • 你是对的,它与整数溢出有关。
  • 还有算术。您需要以相反的顺序“删除”您的属性。这很可能是无用的,甚至适得其反的优化。 hashCode 重要的对象无论如何都应该是不可变的,所以它们不应该有 setter。存储他们的 hashCode 会很有用,因为它永远不会改变。
  • 有道理,谢谢大家。

标签: java math integer integer-overflow


【解决方案1】:

首先:无法正确反转导致整数溢出的操作。基本上问题是您无法知道整数溢出是否在之前的操作中发生过一次、两次甚至更多次。因此,您无法在最后一次哈希计算之前获得原始的value

第二:哈希计算取决于应用哈希值的顺序。

((1 * 37 + 0) * 37 + 1) * 37 + 2 = 50692
((1 * 37 + 2) * 37 + 1) * 37 + 0 = 53428
           ^         ^         ^ the hash values.

由于附加最后一个值的哈希值更改取决于所有先前的哈希值,您不能只更改一个中间哈希值,因此没有(性能良好的)方法可以消除我之前示例中的1 对所有未来的计算。

如果您首先使用123 等而不是1000 测试循环,这应该很明显。如果没有整数溢出,只有以与添加它们相反的顺序删除哈希值时,您的循环才会起作用。这是一个在“现实生活”中没有意义的限制。我认为@JB Nizet 在his comment 中写的内容是正确的。

【讨论】:

  • 谢谢。我知道我在做一些愚蠢的事情,大声笑,显然它永远不会起作用......我想知道避免乘法并只使用 + 和 - 是否是个好主意。这将是可逆的。
  • @Wes 但这会打破平均分配计算生成的哈希值的基本概念。根据设计,具有 (1 和 2) 和 (2 和 1) 子哈希码的两个对象具有不同的哈希码并最终位于不同的桶中。应避免哈希冲突。
  • 确实……但我不确定更糟糕的是,哈希码分布稍微不均匀,或者每次都计算哈希码……我在这个奇怪的想法和记忆之间犹豫不决……这是不可能的,所以现在答案很明显了。非常感谢。
【解决方案2】:

基本上,当用整数计算时,你会做算术模 2^32。因此,在溢出的情况下,如果不是除以 37,而是乘以它的模逆 -1857283155,它就可以工作。例如,这给出了结果 1:

    int pInv = -1857283155;
    int v = 1;

    for (int a = 0; a < 1000; a++) {
        v = v * 37 + a;
    }

    for (int r = 999; r >= 0; r--) {
        v = (v - r) * pInv;
    }

    System.out.println(v);

第二个问题是,加a然后b时的哈希值与加b后a时的值不同。这个哈希函数无法解决这个问题。

【讨论】:

  • 谢谢。我现在可以看到问题了......显然它必须以相反的顺序恢复。
猜你喜欢
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-28
  • 2012-09-16
  • 1970-01-01
  • 2010-12-26
相关资源
最近更新 更多