如何使用 c++ 将字符串散列为 int?
Posted
技术标签:
【中文标题】如何使用 c++ 将字符串散列为 int?【英文标题】:How can I hash a string to an int using c++? 【发布时间】:2011-02-01 21:05:56 【问题描述】:我必须编写自己的哈希函数。如果我只想制作一个简单的哈希函数,将字符串中的每个字母映射到一个数值(即 a=1,b=2,c=3,...),有没有办法可以在一个字符串,而不必先将其转换为 c 字符串来查看每个单独的字符?有没有更有效的哈希字符串方法?
【问题讨论】:
【参考方案1】:您可以使用 []
运算符检查 std::string 中的每个单独的字符。但是,您可以查看 Boost::Functional/Hash 以获得更好的散列方案的指导。 c 中还有一个哈希函数列表,位于here。
【讨论】:
所以,我的理解是哈希函数将字符串映射到 int,但通常这些 int 使用压缩映射映射到表地址,以便哈希表的大小更易于管理。这适用于您在链接中推荐的哈希函数吗? 你是说水桶?有许多“常用”函数在产生的散列表的大小和性能标准方面进行权衡。您应该关心的最大问题是有多少重复值,即您的结果分布有多均匀。糟糕的散列总是会给你留下一小部分链表,而不是一个固定的摊销时间查找表。当我看到 Boost 时,我没有检查过后者。我回答了吗?【参考方案2】:您可以使用字符串类或迭代器的成员函数operator[] 或at 来访问字符串对象的单个字符,而无需将其转换为c 样式的字符数组。
要将字符串对象散列为整数,您必须访问字符串对象的每个单独的字符,您可以这样做:
for (i=0; i < str.length(); i++)
// use str[i] or str.at(i) to access ith element.
【讨论】:
不要在每次迭代时调用str.length()
,尤其是对于在循环期间不会更改的散列字符串。此外,请考虑直接在str.c_str()
上工作,以避免在此进行任何函数调用。字符串确实以NULL
字符结尾。【参考方案3】:
将字符异或在一起,一次四个。
【讨论】:
我真的不明白 xor 是什么/做什么。你能解释一下吗? xor 是一个位运算符,意思是“一个但不是两个”,C++ 中的 '^' 运算符。例如0 ^ 1 => 1 1 ^ 1 => 0 3 ^ 1 => 2 (11 ^ 01 => 10) 它会给你一个随机的整数值。无论哪种方式,您都需要以类似于 Alex Martelli 的解决方案的方式遍历字符串。所以去吧,你不需要担心字的大小。 :) 这不是一个很好的哈希函数。例如,在 ASCII 数据上,它根本不会触及字的第 8、16、24 或 32 位。作为实际效果,如果您的哈希表有 512 个桶,那么其中一半将永远不会被 ASCII 字符串使用。你想在这条线上的某个地方引入一些互质数,并且限制桶数来弥补散列中的弱点只是没有必要考虑到更好的散列的可用性,而且不会慢很多。 公平点。我并不打算将其作为一个好的散列函数,只是一个简单的散列函数。其他答案中的链接描述了许多更好的散列算法。我曾假设(可能是错误地) hash当然是第一个问题,例如:
int hash = 0;
int offset = 'a' - 1;
for(string::const_iterator it=s.begin(); it!=s.end(); ++it)
hash = hash << 1 | (*it - offset);
关于第二个,有很多更好的方法来散列字符串。例如,请参阅 here 了解一些 C 示例(可以按照上面的 sn-p 线轻松转换为 C++)。
【讨论】:
我明白了。如果我想做不区分大小写的散列怎么办。其中 A=a=1? +1,如果仅用于使用*2
和 |
来创建喜剧性差的哈希 ;-)
-1 用于创建可笑的差散列。使用'^',不要使用'|'!即使使用 '^',这也会产生短字符串的不良分布(比您需要的冲突更多)。【参考方案5】:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
// a variation on dan bernstein's algorithm
// [http://www.cse.yorku.ca/~oz/hash.html]
template<typename Int>
struct hash
hash() : acc(5381)
template<typename Ch>
void operator()(Ch ch) acc = ((acc << 5) + acc) ^ ch;
operator Int() const return acc;
Int acc;
;
int main(int argc, char* argv[])
string s("Hellp, world");
cout << hex << showbase
<< for_each(s.begin(), s.end(), hash<unsigned long long>()) << '\n';
return 0;
【讨论】:
【参考方案6】:这是我在 Stroustrup 的书中找到的 C (++) 哈希函数:
int hash(const char *str)
int h = 0;
while (*str)
h = h << 1 ^ *str++;
return h;
如果您将它用于哈希表(Stroustrup 就是这样做的),那么您可以改为返回以素数为模的哈希绝对值。所以改为
return (h > 0 ? h : -h) % N_BUCKETS;
最后一行。
【讨论】:
如果h
是 INT_MIN
,评估 -h
会导致未定义的行为。最好使用无符号数字进行散列。【参考方案7】:
根据个人经验,我知道这很有效并且产生了良好的分布。 (抄袭自http://www.cse.yorku.ca/~oz/hash.html):
djb2
这个算法 (k=33) 是 dan bernstein 多年前在 comp.lang.c 中首次报道的。该算法的另一个版本(现在被 bernstein 青睐)使用 xor:hash(i) = hash(i - 1) * 33 ^ str[i];数字 33 的魔力(为什么它比许多其他常数工作得更好,无论是否素数)从未得到充分解释。
unsigned long hash(unsigned char *str)
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
【讨论】:
【参考方案8】:小字符串的另一种方式:
int hash(const char* str)
int hash = 0;
int c = 0;
while (c < std::strlen(str))
hash += (int)str[c] << (int)str[c+1];
c++;
return hash;
【讨论】:
【参考方案9】:C++11 附带了一个标准的字符串散列函数。
https://en.cppreference.com/w/cpp/string/basic_string/hash
#include <string>
#include<functional> // hash
int main()
std::string s = "Hello";
std::size_t hash = std::hash<std::string>(s);
【讨论】:
【参考方案10】:刚刚发布了对 Arnestig 的 djb2 算法的改进,使其对 constexpr 友好。 我必须删除参数的无符号限定符,以便它可以处理文字字符串。
constexpr unsigned long hash(const char *str)
unsigned long hash = 5381;
while (int c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
【讨论】:
以上是关于如何使用 c++ 将字符串散列为 int?的主要内容,如果未能解决你的问题,请参考以下文章
当我不使用标准字符串时,如何在 C++ 中将字符串转换为 int?
c++如何将string字符串按原内容转换为整型int数据 (C++如何判断某字符串数据是否是纯数字)