【问题标题】:Is there a way to generate a Hash Function which returns hash code of type signed long long?有没有办法生成一个哈希函数,它返回签名 long long 类型的哈希码?
【发布时间】:2021-01-20 01:21:10
【问题描述】:

我需要一个可以将字符串作为输入并从中生成哈希码的函数。目前,在 c++ 中,我们有 std::hash 来执行此操作,但这会返回 size_t(unsigned long long) 类型的哈希码。在这里,我需要一个哈希函数,它可以给我签名 long long 类型的哈希码。

我也尝试过使用模数运算符,但这给了我负值,而且这些值不可靠。因此,请就我可以在 C++ 中使用的散列函数提供建议,以便我得到签名 long long 类型的散列码。

【问题讨论】:

  • 既然hash % n_buckets 不能是负数(不调用Undefined Behavior)——为什么要返回一个有符号数?
  • 取你最喜欢的signed long long toSigned(unsigned long long),并应用到std::hash的结果中
  • @Caleth,在 C++ 中没有名为 toSigned 的函数..
  • @DavidC.Rankin,我试过 (hash % std::numeric_limits::max() ),但这也给了我由于溢出的负值。你能详细解释一下你的逻辑吗?...
  • 使用哈希表时,您有一个 n 指针数组,指向您的表节点类型。指针(元素)的数量称为表的“桶”数量。当您散列一个值时,您会创建一些唯一的数字,然后将其取模 n,例如hash % n。这告诉您节点进入哪个桶(元素)。由于您的节点是链表的节点,因此在发生冲突时,您将节点与桶中的节点链接在一起作为 head 列表中的节点。由于hash % n 不能为负数(没有负数数组索引),所以使用有符号很奇怪。

标签: c++ c++11 hash long-integer


【解决方案1】:

我需要一个哈希函数,它可以给我signed long long 类型的哈希码。

您可以将最高有效位设置为 0,除非您的架构具有奇怪的整数类型内部表示,否则在转换为相同大小的有符号类型时会产生一个正数。

template <class S>
constexpr size_t clamp_to_positive(size_t value)
{
    return value & (std::numeric_limits<size_t>::max() >> 
        (std::numeric_limits<size_t>::digits - std::numeric_limits<S>::digits)
    );
}

你可以这样称呼它

auto my_hash = clamp_to_positive<long long>(std::hash<std::string>{}(source_string));

不过,正如 Ben Voigt 所述,最简单的方法是将无符号值右移一位。

auto my_hash = static_cast<long long>(std::hash<std::string>{}(source_string) >> 1);

解决此问题的另一种方法是强制模运算始终返回正值。

// Evaluates abs(x) as an unsigned type, avoiding corner case overflow.
template <class T>
constexpr auto unsigned_abs(T x)
{
    static_assert(std::is_integral_v<T>);
    if constexpr ( std::is_unsigned_v<T> ) {
        return x;
    }
    return x < 0
        ? ~static_cast<std::make_unsigned_t<T>>(x) + 1 
        : static_cast<std::make_unsigned_t<T>>(x);
}

// Evaluates abs(x) % abs(y) avoiding overflows. The result has the same type
// of y and it's always   0 <= result < y. It has UB when y == 0.
template <class X, class Y> 
auto absolute_remainder(X x, Y y)
{
    static_assert( std::is_integral_v<X> && std::is_integral_v<Y> );
    return static_cast<Y>(unsigned_abs(x) % unsigned_abs(y));
}

您是否真的需要其中之一,或者是否需要更改程序的当前设计,由您自己决定。

【讨论】:

  • 嗨@Bob,请您分解上述逻辑并解释一下。让我看起来有点困惑..
  • @ShailendraPatil 这个想法是获得一个无符号值,以某种方式移动位(这里我只是将最重要的位与较小的位进行异或)以尝试保持哈希的统计属性(其中std::hash 可能很差,具体取决于实现)然后返回一个有符号(但仍然是正数)值。如果您的类型具有二进制补码表示(如今很常见),则屏蔽无符号值的位,删除(设置为零)其最高有效位就足够了。请注意,这不等于 abs()。
  • 生成具有明确 MSB 的数字的更简单方法是(无符号)右移一位。
  • @BenVoigt 好点,谢谢。过度思考解决方案总是一个坏主意。
  • @BenVoigt,对于每个唯一的字符串,通过右移方法生成的哈希码在任何给定时间都是唯一的吗?...
猜你喜欢
  • 2010-10-18
  • 2013-07-29
  • 2011-08-09
  • 2021-07-01
  • 2017-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多