在bit上从bitset到bitset的无序(哈希)映射

时间:2010-10-09 16:16:08

标签: c++ boost hash unordered-map bitset

我想使用一个缓存,由boost的unordered_map实现,从dynamic_bitsetdynamic_bitset。当然,问题是bitset没有默认的哈希函数。它似乎不像一个概念问题,但我不知道如何解决技术问题。我该怎么做?

5 个答案:

答案 0 :(得分:6)

我找到了意想不到的解决方案。事实证明,boost有#define BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS的选项。如果定义了这个,包括m_bits在内的私人成员就会公开(我认为它可以处理旧的编译器或其他东西)。

所以现在我可以使用@ KennyTM的答案,改了一下:

namespace boost {
    template <typename B, typename A>
    std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {            
        return boost::hash_value(bs.m_bits);
    }
}

答案 1 :(得分:3)

to_block_range函数将复制 outset由bitset组成的单词放入某个缓冲区。为了避免实际复制,您可以定义自己的“输出迭代器”,它只处理单个单词并从中计算哈希值。回覆。如何计算哈希:参见例如FNV哈希函数。

不幸的是,dynamic_bitset的设计是恕我直言,因为它不能让你直接访问底层缓冲区(甚至不是const)。

答案 2 :(得分:3)

这是feature request

通过将bitset转换为临时向量,可以实现效率不高的唯一哈希:

namespace boost {
    template <typename B, typename A>
    std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {
        std::vector<B, A> v;
        boost::to_block_range(bs, std::back_inserter(v));
        return boost::hash_value(v);
    }
}

答案 3 :(得分:2)

我们无法直接计算哈希,因为dynamic_bitset中的基础数据是私有的(m_bits

但我们可以很容易地在没有

的情况下过去(颠覆!)c ++访问规范系统
  • 黑客攻击代码或
  • 假装您的编译器不符合(BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS

关键是模板函数to_block_range,它是frienddynamic_bitset。因此,该功能的专业化也可以访问其私人数据(即m_bits)。

生成的代码不能简单

namespace boost {


// specialise dynamic bitset for size_t& to return the hash of the underlying data
template <>
inline void
to_block_range(const dynamic_bitset<>& b, size_t& hash_result)
{
    hash_result = boost::hash_value(bs.m_bits);
}

std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) 
{            
    size_t hash_result;
    to_block_range(bs, hash_result);
    return hash_result;
}
}

答案 4 :(得分:0)

建议的解决方案在以下情况下生成相同的哈希值。

#define BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS

namespace boost {
    template <typename B, typename A>
    std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {            
        return boost::hash_value(bs.m_bits);
    }
}

boost::dynamic_biset<> test(1,false);

auto hash1 = boost::hash_value(test);

test.push_back(false);

auto hash2 = boost::hash_value(test);

// keep continue...

test.push_back(false);

auto hash31 = boost::hash_value(test);

// magically all hash1 to hash31 are the same!

建议的解决方案有时候对哈希映射不合适。

我阅读了dynamic_bitset的源代码,为什么会发生这种情况,并意识到dynamic_bitset每个值存储一位与vector<bool>相同。例如,您调用dynamic_bitset<> test(1, false),然后dynamic_bitset最初分配4个字节全部为零,并且它保持位的大小(在这种情况下,大小为1)。注意,如果位的大小大于32,则它再次分配4个字节并将其推回dynamic_bitsets<>::m_bits(因此m_bits是4个字节块的向量)。

如果我调用test.push_back(x),它会将第二位设置为x并将位大小增加到2.如果x为false,则m_bits[0]根本不会更改!为了正确计算哈希值,我们需要在哈希计算中使用m_num_bits。

然后,问题是如何?

1:使用boost::hash_combine 这种方法简单直接。我没有检查这个编译与否。

namespace boost {
    template <typename B, typename A>
    std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) { 
        size_t tmp = 0;
        boost::hash_combine(tmp,bs.m_num_bits);           
        boost::hash_combine(tmp,bs.m_bits);
        return tmp;
    }
}

2:翻转m_num_bits%bits_per_block th bit。 根据位大小翻转一下。我相信这种方法比1快。

namespace boost {
    template <typename B, typename A>
    std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {
        // you may need more sophisticated bit shift approach.
        auto bit = 1u << (bs.m_num_bits % bs.bits_per_block);
        auto return_val = boost::hash_value(bs.m_bits);

       // sorry this was wrong
       //return (return_val & bit) ? return_val | bit : return_val & (~bit);
       return (return_val & bit) ? return_val & (~bit) : return_val | bit;
    }
}
相关问题