字符串hash相关
哈希
c++里常用的hash是map和unordered_map
前者是平衡树实现的, O(logn)的插入和搜索, 后者是O(1)的插入和搜索
但是前者有序, 后者无序
本文讲的是后者
关于实现
基本类型可以视所需空间大小选择不同的hash办法
而我着重讲一下字符串的hash
在字符串hash里
- DJB hash
- SDBM hash
- BKDR hash
这些都是常见的hash
关于空间
这里提一下, hash有"负载因子"一说
负载因子 衡量哈希表满的程度的指标, 当元素数量/桶数量 > 负载因子时, hash表会进行重新hash
用unordered_map的时候一开始就要reverse扩容到自己需要的空间, 不然随着元素增多, 他会rehash, 浪费性能
自己写hash表的时候, 我听说一般都是直接开4N的空间, 原理未知, 可能是性价比比较高吧, 在我看来, 肯定是空间允许的话, 开的越大越好
关于碰撞
碰撞我一般直接直接链起来, 来判断即可.
比较简单
hash算法碰撞测试
测一下两个简单的hash, djb和bkdr
#include <bits/stdc++.h> #include <chrono> using namespace std; #define HASH_SIZE 100000 #define HASH_TABLE_SIZE (HASH_SIZE << 2) #define MOD (HASH_TABLE_SIZE) int m_count[HASH_TABLE_SIZE]; vector<string> generate_string(int num, int max_length) { vector<string> strings; strings.reserve(num); random_device rd; mt19937 gen(rd()); uniform_int_distribution<> length_dist(1, max_length); uniform_int_distribution<> char_dist(32, 126); for (int i = 0; i < num; i++) { int len = length_dist(gen); string str; str.reserve(len); for (int j = 0; j < len; j++) { str.push_back(static_cast<char>(char_dist(gen))); } strings.push_back(str); } return strings; } unsigned long long BKDRhash(char *c) { unsigned long long seed = 131; unsigned long long hash = 0; while (*c) { hash *= seed; hash += (*c++); } return hash % MOD; } unsigned long long DJBhash(char *c) { unsigned long long hash = 5381; while (*c) { hash += (hash << 5); hash += (*c++); } return hash % MOD; } void test(std::function<unsigned long long(char *c)> f, std::vector<string> &strings) { memset(m_count, 0, sizeof(m_count)); auto start = std::chrono::high_resolution_clock::now(); for (auto &str : strings) { m_count[f(&str[0])]++; } int collision = 0; for (int i = 0; i < HASH_TABLE_SIZE; i++) { if (m_count[i] > 1) { collision++; } } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); std::cout << "Time taken by function: " << duration.count() << " milliseconds" << std::endl; cout << "collision: " << collision << endl; } int main() { vector<string> strs = generate_string(100000, 1000); test(BKDRhash, strs); test(DJBhash, strs); return 0; }
上面代码跑出来结果是
Time taken by function: 144 milliseconds collision: 10659 Time taken by function: 161 milliseconds collision: 10696
然后我把哈希表又放大了4倍, 果然跟我想的一样, 碰撞少了
collision: 2934 collision: 3059
那其实没有什么空间要开多大的说法, 本来就是允许的话越大越好.
而且bkdr的hash性能似乎更好一些, 虽然我也看不懂为什么乘法会比位移更好
不懂, 反正事实如此, 开完O2后两个差不多, 那看来可能是没优化吧
seed的选择也相当无所谓, 我是没测出来素数好在哪
posted on 2023-11-23 15:38 tianlonghuangwu 阅读(11) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现