重要通知:
对于人们来说,这不是一个讨论线程,可以让我对哈希有所了解。我只需要知道如何使给定的函数在java中工作 - 一个例子就是最好的。
问题:
我试图通过麻省理工学院计算机科学教授(http://videolectures.net/mit6046jf05_leiserson_lec08/)观看两个免费讲座,以便磨练我对哈希函数的理解。所以在讲座之后,我试图在java中实现以下哈希函数。
h(k) = (A·k mod 2^w) >> (w – r)
WHERE
r: m, the size of the array, is a power of 2 such that m=2^r
w: the computer has w-bit words, such as 32-bit or 64-bit computer
k: the value I am to find a key for
A: a random odd number (prime would be great) between 2^(w-1) and 2^w
我认为这很容易在java中实现。但是当我做2 ^ w,其中w = 32时,我在Java中得到的结果不准确。在现实生活中2^32 = 4294967296
但不在java中,将结果截断为2^31 - 1
或2147483647
。
有谁知道如何解决这个问题,以便在Java中实现该功能?
编辑:
我看到很多回复集中在32.如果我的电脑是64位怎么办?我很难设置w = 32
,因为我使用的是Java?
答案 0 :(得分:4)
有些术语是多余的,因为Java无论如何都会采用这种行为。
A·k mod 2^w
在Java中,整数乘法溢出,因此执行mod 2^w
(带符号)。如果你移动了至少一位,它有一个符号的事实并不重要。
(w - r)
的移位与Java中-r
的移位相同(类型隐含w)
private static final int K_PRIME = (int) 2999999929L;
public static int hash(int a, int r) {
// return (a * K_PRIME % (2^32)) >>> (32 - r);
return (a * K_PRIME) >>> -r;
}
表示64位
private static final long K_PRIME = new BigInteger("9876534021204356789").longValue();
public static long hash(long a, int r) {
// return (a * K_PRIME % (2^64)) >>> (64 - r);
return (a * K_PRIME) >>> -r;
}
我写了这个例子,表明你可以在BigInteger中做同样的事情,为什么你不这样做。 ;)
public static final BigInteger BI_K_PRIME = new BigInteger("9876534021204356789");
private static long K_PRIME = BI_K_PRIME.longValue();
public static long hash(long a, int r) {
// return (a * K_PRIME % (2^64)) >>> (64 - r);
return (a * K_PRIME) >>> -r;
}
public static long biHash(long a, int r) {
return BigInteger.valueOf(a).multiply(BI_K_PRIME).mod(BigInteger.valueOf(2).pow(64)).shiftRight(64 - r).longValue();
}
public static void main(String... args) {
Random rand = new Random();
for (int i = 0; i < 10000; i++) {
long a = rand.nextLong();
for (int r = 1; r < 64; r++) {
long h1 = hash(a, r);
long h2 = biHash(a, r);
if (h1 != h2)
throw new AssertionError("Expected " + h2 + " but got " + h1);
}
}
int runs = 1000000;
long start1 = System.nanoTime();
for (int i = 0; i < runs; i++)
hash(i, i & 63);
long time1 = System.nanoTime() - start1;
long start2 = System.nanoTime();
for (int i = 0; i < runs; i++)
biHash(i, i & 63);
long time2 = System.nanoTime() - start2;
System.out.printf("hash with long took an average of %,d ns, " +
"hash with BigInteger took an average of %,d ns%n",
time1 / runs, time2 / runs);
}
打印
hash with long took an average of 3 ns, \
hash with BigInteger took an average of 905 ns
答案 1 :(得分:2)
int
和long
都不足以容纳2 ^(w-1)中所需的所有值。您最好使用BigInteger
。
答案 2 :(得分:1)
让我们看看number % 2^32
实际上做了什么:它得到除法的余数2 ^ 32。如果您的范围是0到2 ^ 32,计算机将自动为您执行模数,因为它会丢弃2 ^ 32以上的所有内容。
让我们取8而不是32,并切换到二进制数系统:
1000 1000 % 1 0000 0000 = 1000 1000
1 1000 1000 % 1 0000 0000 = 1000 1000
所以你应该做的是将数量限制在计算机的范围内。如果您愿意使用,例如c ++,就像将值声明为unsigned int
一样简单。上面第二个示例的第一个1
将被截断,因为它不适合变量。
在java中,您没有无符号整数。如果您计算A * k
,并且导致溢出,则可能会获得签名值。但是,接下来你唯一需要做的就是做一个正确的转变,这应该不重要。
所以我的建议是简单地放弃模数计算。尝试一下,我不太确定它是否有效。
答案 3 :(得分:0)