Java 将哈希值映射到一个范围,冲突最少 上下文

Java 将哈希值映射到一个范围,冲突最少 上下文,java,hash,hashtable,Java,Hash,Hashtable,嗨,我正在为学校做一个作业,要求我们用Java实现一个哈希表。没有将碰撞保持在最低限度的要求,但在我所做的所有工作中,低碰撞率和低碰撞速度似乎是两个最受欢迎的品质 问题 我想要一些关于如何将散列函数的输出映射到一个较小范围的指导,而不让>20%的键发生冲突(yikes) 在我所研究的所有算法中,键都映射到无符号32位整数的整个范围(或者在许多情况下是64位,甚至128位)。我在这里、维基百科或我遇到的任何与哈希相关的文章/讨论中都找不到太多关于这方面的内容 就我实现的细节而言,我使用Java(我

嗨,我正在为学校做一个作业,要求我们用Java实现一个哈希表。没有将碰撞保持在最低限度的要求,但在我所做的所有工作中,低碰撞率和低碰撞速度似乎是两个最受欢迎的品质

问题 我想要一些关于如何将散列函数的输出映射到一个较小范围的指导,而不让>20%的键发生冲突(yikes)

在我所研究的所有算法中,键都映射到无符号32位整数的整个范围(或者在许多情况下是64位,甚至128位)。我在这里、维基百科或我遇到的任何与哈希相关的文章/讨论中都找不到太多关于这方面的内容

就我实现的细节而言,我使用Java(我学校的授权),这是有问题的,因为没有未签名的类型可以使用。为了解决这个问题,我一直在使用64位长的整数类型,然后使用位掩码映射回32位。我不是简单地截断,而是将最上面的32位与最下面的32位进行异或运算,然后执行逐位AND运算,以屏蔽掉当我将其向下转换为32位整数时可能导致负值的任何高位。在所有这些之后,一个单独的函数将结果哈希值向下压缩,以适应哈希表内部数组的边界

它最终看起来像:

int hash( String key ) {

    long h;

    for( int i = 0; i < key.length(); i++ )
        //do some stuff with each character in the key

        h = h ^ ( h << 32 );

    return h & 2147483647;
}
其中p是小于内部数组大小的最大素数


这是一种公认的压缩哈希值的方法吗?我有一种感觉,它不是,但由于在压缩之前性能就很差,我也有一种感觉,它不是主要的罪魁祸首。

我不知道我是否理解您的具体问题,但我将尝试在哈希性能和冲突方面提供帮助

基于散列的对象将根据散列值确定它们将在哪个bucket中存储键值对。在每个bucket中都有一个存储对的结构(在HashMap中是LinkedList)

如果散列值通常相同,则bucket通常相同,因此性能会降低很多,让我们看一个示例:

以这门课为例

package hashTest;

import java.util.Hashtable;

public class HashTest {

    public static void main (String[] args) {

        Hashtable<MyKey, String> hm = new Hashtable<>();

        long ini = System.currentTimeMillis();

        for (int i=0; i<100000; i++) {
            MyKey a = new HashTest().new MyKey(String.valueOf(i));

            hm.put(a, String.valueOf(i));
        }

        System.out.println(hm.size());

        long fin = System.currentTimeMillis();
        System.out.println("tiempo: " + (fin-ini) + " mls");
    }

    private class MyKey {

        private String str;

        public MyKey(String i) {
            str = i;
        }

        public String getStr() {
            return str;
        }

        @Override
        public int hashCode() {
            return 0;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof MyKey) {
                MyKey aux = (MyKey) o;
                if (this.str.equals(aux.getStr())) {
                    return true;
                }
            }
            return false;
        }
    }
}
性能非常差,现在我们要更改MyKey哈希代码:

package hashTest;

import java.util.Hashtable;

public class HashTest {

    public static void main (String[] args) {

        Hashtable<MyKey, String> hm = new Hashtable<>();

        long ini = System.currentTimeMillis();

        for (int i=0; i<100000; i++) {
            MyKey a = new HashTest().new MyKey(String.valueOf(i));

            hm.put(a, String.valueOf(i));
        }

        System.out.println(hm.size());

        long fin = System.currentTimeMillis();
        System.out.println("tiempo: " + (fin-ini) + " mls");
    }

    private class MyKey {

        private String str;

        public MyKey(String i) {
            str = i;
        }

        public String getStr() {
            return str;
        }

        @Override
        public int hashCode() {
            return str.hashCode() * 31;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof MyKey) {
                MyKey aux = (MyKey) o;
                if (this.str.equals(aux.getStr())) {
                    return true;
                }
            }
            return false;
        }
    }
}
有一个令人难以置信的更好的表现,现在有一个小的变化。返回哈希代码乘以素数(在本例中为31)是一种非常常见的做法,使用与equals方法中相同的哈希代码成员来确定两个对象是否相同(在本例中为str)


我希望这个小例子能为你的问题指出解决办法

您希望将440K值填充到多大的数组中?大于20%的键冲突有什么问题?你感兴趣的不是把很多东西放在一个单独的存储桶中,也不是让散列变得独一无二。我用一个数组来衡量性能,这个数组的大小是我需要散列的元素数量的两倍。我的教授说1%或低至100次冲突是一个理想的比率。因为Java 7,你可以使用
Objects.hash(Object…values)
快速高效地创建一个散列。显然,我并不期望达到或超过本机进行的散列。这是大学的作业,我们必须从头做起。
100000 
tiempo: 62866 mls
package hashTest;

import java.util.Hashtable;

public class HashTest {

    public static void main (String[] args) {

        Hashtable<MyKey, String> hm = new Hashtable<>();

        long ini = System.currentTimeMillis();

        for (int i=0; i<100000; i++) {
            MyKey a = new HashTest().new MyKey(String.valueOf(i));

            hm.put(a, String.valueOf(i));
        }

        System.out.println(hm.size());

        long fin = System.currentTimeMillis();
        System.out.println("tiempo: " + (fin-ini) + " mls");
    }

    private class MyKey {

        private String str;

        public MyKey(String i) {
            str = i;
        }

        public String getStr() {
            return str;
        }

        @Override
        public int hashCode() {
            return str.hashCode() * 31;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof MyKey) {
                MyKey aux = (MyKey) o;
                if (this.str.equals(aux.getStr())) {
                    return true;
                }
            }
            return false;
        }
    }
}
100000
tiempo: 47 mls