C++ C++;:关于字符串序列的哈希函数的建议,其中字符串的顺序无关

C++ C++;:关于字符串序列的哈希函数的建议,其中字符串的顺序无关,c++,hash,map,sequence,string-hashing,C++,Hash,Map,Sequence,String Hashing,假设你有这两个字符串序列 abc cba bc bc abc cba 我试图为这些序列创建一个映射(序列也是一个字符串),以便将上述两个序列映射到同一个bucket中 我最初的想法是添加分别应用于每个字符串的哈希函数的结果。这样一来,他们的顺序就无关紧要了。如果我将散列函数作为一个整体应用于序列字符串,那么散列结果当然会不同 然而,我对字符串哈希函数的世界非常陌生,我不知道这种方法是否有效 在本网站 我发现了许多不同的字符串哈希实现,但是我不确定哪一个是最适合我需要的 关于序列中每个字符串的一些

假设你有这两个字符串序列

abc cba bc

bc abc cba

我试图为这些序列创建一个映射(序列也是一个字符串),以便将上述两个序列映射到同一个bucket中

我最初的想法是添加分别应用于每个字符串的哈希函数的结果。这样一来,他们的顺序就无关紧要了。如果我将散列函数作为一个整体应用于序列字符串,那么散列结果当然会不同

然而,我对字符串哈希函数的世界非常陌生,我不知道这种方法是否有效

在本网站

我发现了许多不同的字符串哈希实现,但是我不确定哪一个是最适合我需要的

关于序列中每个字符串的一些技术细节是,每个字符串的长度不超过25个字符。此外,每个序列的字符串不会超过3个

问题

1.
将字符串哈希函数的结果添加到序列的每个字符串中的方法是否有效

2.
如果是,我应该使用哪种字符串哈希函数,以减少冲突量并节省时间


提前感谢您

仅是想法演示(非常低效的字符串复制),复杂性O(NlogN),其中N是键的大小(==O(1),如果您的键在编译时具有已知的恒定长度),我认为您无法实现更好的复杂性:

#include <boost/functional/hash.hpp>
#include <set>
#include <algorithm>

std::size_t make_hash(
  std::string const& a,
  std::string const& b,
  std::string const& c)
{
    std::string input[] = {a,b,c};
    std::sort(input, input + (sizeof(input)/sizeof(*input)));
    return boost::hash_range(input, input + (sizeof(input)/sizeof(*input)));
}

#include <iostream>
// g++ -I.../boost_1_47_0 string_set_hash.cpp
int main()
{
    std::cout << make_hash("abc", "bcd", "def") << std::endl; // 46247451276990640
    std::cout << make_hash("bcd", "def", "abc") << std::endl; // 46247451276990640
}
#包括
#包括
#包括
标准::大小\u t生成\u散列(
标准::字符串常量&a,
标准::字符串常量和b,
标准::字符串常量(c)
{
字符串输入[]={a,b,c};
排序(输入,输入+(sizeof(输入)/sizeof(*输入));
返回boost::hash_范围(输入,输入+(sizeof(输入)/sizeof(*输入));
}
#包括
//g++-I../boost\u 1\u 47\u 0 string\u set\u hash.cpp
int main()
{

std::cout无论选择什么散列函数,都需要为每个散列的最终组合使用一个运算符,即:

  • 交换的
  • 联想的
和、积和异或是积分值的候选者。是的,加法是可行的。但是,在需要解决的不相关序列上仍然会有冲突,因此需要一个字符串比较函数,但同一组字符串的排列将在同一个桶中结束

您也可以颠倒操作顺序:首先将字符串按字符顺序添加在一起(例如,将“ab”和“cba”相加后变成('a'+'c')('b'+'b')('\0'+'a'),并对和或积进行进位传播,因此这里可能需要使用xor),然后应用散列函数。您甚至可以在执行这两个操作时将其组合(伪代码如下所示):

int散列(字符串a、字符串b、字符串c){
int r=0,k;
int m=max(a.length(),max(b.length(),c.length());
for(int i=0;i
使用
散列
增量散列函数。对于足够大的素数(即大于bucket数组的预期大小)进行简单的模运算应该可以正常使用


一个完全不同的(更好的?)解决方案是简单地对序列进行排序(3个条目表示准常量时间),然后使用比较函数制作一个有序映射,将字符串视为3位数字的“数字”。但这超出了问题的范围。

我将逐个散列每个元素

然后对这些散列进行排序。排序3
size\t
很快

然后链接这些散列。您的库可能有散列链函数,甚至可以使用带有溢出包装的
散列(a+b+c)


避免异或,因为异或两个相同的哈希值为零。相同字符串的哈希值相同。因此,简单的异或可能导致
(a,a,b)
(c,c,b)
具有相同的散列输出,这很糟糕。

将散列函数应用于字符串序列的排序副本是否有用?字母表的大小是多少(即将使用什么字符集)?您希望它们在同一个存储桶中,但不要冲突?要求很高。如果您对序列进行排序,您甚至不需要哈希,只需比较具有相同秩的字符串。此外,我建议使用XOR。感谢您的建议,以我描述的方式实现您自己的哈希函数不会避免额外的排序成本吗?因为字符串的哈希值至少为O(N),但考虑到我最多可以对序列的每个字符串使用三次哈希函数,这将给出O(Ki)复杂性,其中I是序列的第I个字符串,总体性能将为O(K1+K2+…)=O(N)。为什么这比使用对称操作(如加法)组合单个字符串哈希要好?@MikeSeymour-如果你证明加法可以保持统一的密钥分布,我很乐意删除我的answer@bobah当前位置我并不是说答案是错的,我只是想看看增加复杂性的理由。(我没有时间证明这一点,但我非常肯定独占或将保留发行版;我会使用它,而不是添加)@MikeSeymour-我相信boost hash library writer是优秀哈希函数的专家,并使用boost::hash的现有API提出了答案。我已经添加了一个关于复杂性的说明,如果密钥大小较小且固定,那么XOR-ing的排序是额外的NlogN vs N。虽然有3个项,但每个项的大小都是无限的:在这种情况下,您需要我想读每个字符最多一次。
template <class T>
inline void hash_combine(std::size_t& seed, T const& v)

{
    boost::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

template <class It>
inline std::size_t hash_range(It first, It last)
{
    std::size_t seed = 0;

    for(; first != last; ++first)
    {
        hash_combine(seed, *first);
    }

    return seed;
}
int hash(string a, string b, string c){
    int r = 0, k;
    int m = max(a.length(), max(b.length(), c.length()));
    for (int i = 0; i < m; i++) {
        k = ( i < a.length()? a[i] : 0) ^
              (i < b.length()? b[i] : 0) ^
              (i < c.length()? c[i] : 0);
        r = hash(r,k);
    }
    return r;
}