首页
社区
课程
招聘
[求助]hash函数
2023-10-13 17:20 5566

[求助]hash函数

2023-10-13 17:20
5566

大佬们我有一段java代码,是hash表的demo实现,我在寻找一个hash函数,可以让hash表的空槽率降低到40%,我目前的hash函数只能降低到47.29%,hash表大小16777216,插入元素1000万,数据样本递增,hash算法检测如下:
====== Hash算法检测 ======
为空概率(%): 47.29578495025635,空槽位数: 7934916
为链概率(%): 52.70421504974365,链槽位数: 8842300
为树概率(%): 0.0,树槽位数: 0
总数: 16777216, 冲突层数平均: 2.130927473621117
====== Hash算法检测 ======
请大佬们找一下更好的hash函数,我试了很多hash函数都不太行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
public final class Demo {
    private static int cap;
 
    private static int size = 0;
 
    private static Node[] map;
 
    private static final int LINK = 0;
 
    public Demo(int cap) {
        cap = _tableSizeFor(cap);
        Demo.cap = cap;
        map = new Node[cap];
    }
 
    public void put(byte[] key, long val) {
        _put(key, val, cap, map);
    }
 
    private static void _put(byte[] key, long val, int cap, Node[] map) {
        long hash = _hash(key);
        int index = _getIndex(hash, cap);
        Node head;
        if ((head = map[index]) == null) {
            map[index] = new Node(key, val, hash);
        } else {
            if (head.type == LINK) {
                int size = 1;
                Node p;
                do {
                    ++size;
                    if (head.hash == hash && Arrays.equals(key, head.key)) {
                        head.val = val;
                        return;
                    }
                    p = head;
                    head = head.next;
                } while (head != null);
                p.next = new Node(key, val, hash);
                if (size == 8) {
                    System.out.println("转树了");
                }
            } else {
                System.out.println("树插入");
            }
            if (++Demo.size == cap) _expansion();
        }
    }
 
    private static void _expansion() {
        Node[] newMap = new Node[cap << 1];
        for (int i = 0; i < cap; ++i) {
            Node c = map[i];
            if (c != null) {
                Node next;
                do {
                    next = c.next;
                    c.next = null;
                    _put(c.key, c.val, cap << 1, newMap);
                    c = next;
                } while (c != null);
            }
        }
        cap <<= 1;
        map = newMap;
    }
 
    private static int _tableSizeFor(int cap) {
        --cap;
        cap |= cap >>> 1;
        cap |= cap >>> 2;
        cap |= cap >>> 4;
        cap |= cap >>> 8;
        cap |= cap >>> 16;
        return (cap < 0) ? 1 : cap + 1;
    }
 
    //  3               4198            1000W   47.29578495025635
    //  5               4198            1000W   47.44309186935425
    //  20              4198            1000W   47.55440950393677
    //  10              4198            1000W   47.58404493331909
    //  26              4198            1000W   47.679245471954346
    //  19              4198            1000W   47.803592681884766
    //  15              4198            1000W   47.8635311126709
    //  1               4198            1000W   47.87636995315552
    //  21              4198            1000W   47.886037826538086
    //  14              4198            1000W   47.95495271682739
    //  378551          435             1000W   48.08
    //  0x100000001b3L  0x100000001b3L  1000W   48.30
    //  1               83535           1000W   48.35
    //  0               23781           300W    44
    //  485103
    private static long _hash(byte[] bytes) {
        long result = 3;
        for (byte b : bytes)
            result = 4198 * result + b;
        return result;
    }
 
    private static int _getIndex(final long hash, final int cap) {
        return (int) ((cap - 1) & hash);
    }
 
    static final class Node {
        byte[] key;
        long val;
        long hash;
        int type = LINK;
        Node next;
 
        Node(byte[] key, long val, long hash) {
            this.key = key;
            this.val = val;
            this.hash = hash;
        }
    }
 
    public static void main(String[] args) {
        Demo demo = new Demo(10000000);
        for (int i = 0; i < 10000000; ++i) {
            demo.put(String.valueOf(i).getBytes(StandardCharsets.UTF_8), i);
        }
        System.out.println("====== Hash算法检测 ======");
        int emptySlotCount = 0, turnTreeCount = 0, avgLinkSize = 0;
        final int cap = Demo.cap;
        for (int i = 0; i < cap; ++i) {
            Node head = map[i];
            if (head == null) {
                ++emptySlotCount;
            } else {
                if (head.type == LINK) {
                    int linkSize = 1;
                    do {
                        ++linkSize;
                        head = head.next;
                    } while (head != null);
                    if (linkSize != 8) avgLinkSize += linkSize;
                } else {
                    ++turnTreeCount;
                }
            }
        }
        System.out.println("为空概率(%): " + (1D * emptySlotCount / cap * 100) + ",空槽位数: " + emptySlotCount);
        System.out.println("为链概率(%): " + (1D * (cap - turnTreeCount - emptySlotCount) / cap * 100 + ",链槽位数: " + (cap - turnTreeCount - emptySlotCount)));
        System.out.println("为树概率(%): " + (1D * turnTreeCount / cap * 100) + ",树槽位数: " + turnTreeCount);
        System.out.println("总数: " + cap + ", 冲突层数平均: " + (1D * avgLinkSize / (cap - turnTreeCount - emptySlotCount)));
        System.out.println("====== Hash算法检测 ======");
    }
}

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (11)
雪    币: 248
活跃值: (1031)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sixL 2023-10-13 23:09
2
0
//;第一个参数是要计算HASH的数据地址,第二个参数是数据的长度(单位是字节数)
HashValue proc uses rbx pData:qword,dqSize:qword    ;hashValue
    ; retValue=rax
    mov    rdx,pData
    mov    rcx,dqSize
    mov    rax,0

    jecxz  @4
    not    rax
@1:                    
    xor    al, [rdx]
    inc    rdx
    mov    bl, 8
@2:                    
    shr    rax, 1
    jnc    @3
    xor    rax, 095AC9329AC4BC9B5h    
@3:                    
    dec    bl
    jnz    @2
    loop   @1

    not    rax
@4:             
    ret

HashValue endp
最后于 2023-10-13 23:12 被sixL编辑 ,原因: 修改
雪    币: 69
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
aress 2023-10-14 08:40
3
0
sixL //;第一个参数是要计算HASH的数据地址,第二个参数是数据的长度(单位是字节数) HashValue&nbsp;proc&nbsp;uses&nbsp;rbx&nb ...
大佬你这是汇编吗?有点看不懂
雪    币: 248
活跃值: (1031)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sixL 2023-10-14 21:09
4
0

你这java运行在64bit WINDOWS,还是Linux,Mac OS 上?如果是64bit WINDOWS,给你lib或DLL,可否测试一下,碰撞是否能降低?

或者

    private static long _hash(byte[] bytes) {
        long result = 3;
        for (byte b : bytes)
            result = 4198 * result + b;
        return result;
    }

==>

32bit:16777619(大素数)
64bit:1099511628211(大素数)
    private static long _hash(byte[] bytes) {
        long result = 3;
        for (byte b : bytes)
            result = 1099511628211  * result + b;
        return result;
    }

最后于 2023-10-15 08:20 被sixL编辑 ,原因:
雪    币: 69
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
aress 2023-10-16 11:25
5
0
sixL 你这java运行在64bit&nbsp;WINDOWS,还是Linux,Mac&nbsp;OS&nbsp;上?如果是64bit&nbsp;WINDOWS,给你lib或D ...
1099511628211这个数可以做到48.08%,不过还是不行,我自己找到了一个可以降到40的,不过hash算法1看数据样本,2看函数性质,我这个函数性质我觉得有点怪,很反直觉,但是性质有比较厉害说不清楚,大佬能帮我看看为啥吗
    private static long _hash(byte[] bytes) {
        long r = 1;
        for (byte b : bytes) r = 10 * r + b;
        return r;
    }
雪    币: 69
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
aress 2023-10-16 11:28
6
0
说错了是48.05%
====== Hash算法检测 ======
为空概率(%): 48.05457592010498,空槽位数: 8062220
为链概率(%): 51.94542407989502,链槽位数: 8714996
为树概率(%): 0.0,树槽位数: 0
总数: 16777216, 冲突层数平均: 2.147447457233486
====== Hash算法检测 ======
雪    币: 29
活跃值: (5115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
不吃早饭 2023-10-16 11:35
7
0
xxhash
雪    币: 69
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
aress 2023-10-16 13:12
8
0
不吃早饭 xxhash
这个不行,很多经典hash我都试了,都不太行
雪    币: 10845
活跃值: (1044)
能力值: (RANK:190 )
在线值:
发帖
回帖
粉丝
看场雪 3 2023-10-17 15:36
9
0
如果LZ愿意多付出一点计算代价,可以将hash表的利用率提高到90%以上,且保持较好的查找效率
雪    币: 69
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
aress 2023-10-17 18:50
10
0
看场雪 如果LZ愿意多付出一点计算代价,可以将hash表的利用率提高到90%以上,且保持较好的查找效率
怎么计算,我目前找到一个性质非常好的hash函数,但我不确定你所说的方法是否比我这个好,    private static long _hash(byte[] bytes) {
        long r = 1;
        for (byte b : bytes) r = 10 * r + b;
        return r;
    }
雪    币: 10845
活跃值: (1044)
能力值: (RANK:190 )
在线值:
发帖
回帖
粉丝
看场雪 3 2023-12-19 09:14
11
0
aress 怎么计算,我目前找到一个性质非常好的hash函数,但我不确定你所说的方法是否比我这个好, private static long _hash(byte[] bytes) { l ...
在使用hash表的时候,一般都需要均匀、随机的hash函数
通常在非密码类应用中,不太要求‘抗弱/强碰撞’
所以,一般只强调随机碰撞概率,而不防恶意碰撞;只强调算法效率,而不太多考虑安全

上面的话只是一般性描述,与‘可以将hash表的利用率提高到90%以上’关系不大
即便是使用了好的hash算法,一般利用率也只能在30%左右。太高之后会严重降低效率。

真正能提高利用率的是cuckoo hash表。希望对你有用
雪    币: 432
活跃值: (152)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lovepd 2024-1-4 15:44
12
0
如果不考虑存储空间的情况下,可以引入多个hash表来降低碰撞机率。
假如单个hash表的碰撞机率为50%,引入三个hash表的碰撞机率理论上可以做到50%*50%*50%=12.5%。

a = hash(val, 1); // 给表1用的hash
b = hash(val, 2); // 给表2用的hash
c = hash(val, 3); // 给表3用的hash

暴雪魔兽争霸的mqp文件,就是用的类似方式来降低同一文件的碰撞机率的。

希望对你有所帮助!
游客
登录 | 注册 方可回帖
返回