【问题标题】:Hash function for strings without respecting char's position不考虑字符位置的字符串哈希函数
【发布时间】:2015-03-14 07:04:46
【问题描述】:

我的问题的标题是自我描述的。 我需要散列三个 64 位变量的结构(我会将它们转换为一串字符),每个变量都包含一手牌 - 纸牌游戏应用程序,因此在这些变量中交换一些字符应该会产生相同的散列。 一种方法是对结果字符串进行排序。有没有更好的解决办法?

【问题讨论】:

  • 我猜唯一的方法是将哈希函数基于字符集中每个字符的频率
  • 用谷歌搜索Zobrist hashing

标签: algorithm hash hashtable


【解决方案1】:

如果手的表示类似于位集,则它已经是无序的。例如,如果您使用位掩码的组合来表示卡片的组合,比如说,像这样

A♠ - 0x00000001
2♠ - 0x00000002
3♠ - 0x00000004
4♠ - 0x00000008
...
K♠ - 0x00001000
A♥ - 0x00002000
2♥ - 0x00004000
...

然后您可以使用位组合来表示手,如下所示:

A♠ 4♠ 2♥ - 0x00004009

此表示与位置无关,即手4♠ A♠ 2♥2♥ 4♠ A♠ 将具有与A♠ 4♠ 2♥ 完全相同的表示。您可以根据需要将此表示转换为字符串,方法是迭代各个位,并在每次发现设置为 1 的位时将卡片添加到字符串表示中。

通过将表示的高 32 位与低 32 位进行异或运算,可以使用这样的表示来计算 32 位哈希码:

uint64_t hand = ... // A representation of hand similar to what's described above
uint32_t hash = (uint32_t)(hand ^ (hand >> 32));

目前我的卡片以字节表示,但两张卡片中的位可以重叠:A♣ = 0x11; 10♣=0x12; K♣=0x13 ... 等等。

您可以在计算哈希码时将此表示转换为上述表示,并避免以这种方式排序:

// Each card is a number from 1 to 53, inclusive
uint8_t hand[HAND_SIZE] = ...; // The hand
uint64_t set = 0;
for (int i = 0 ; i != HAND_SIZE ; i++) {
    set |= (1LL << hand[i]);
}
uint32_t hash = (uint32_t)(set ^ (set >> 32));

【讨论】:

  • uint32_t hash = (uint32_t)(set ^ (set &gt;&gt; 32)); 两张卡(位位置相差 32 位)将哈希到相同的值
  • @wildplasser 为什么?花色相隔 13 位,13 不除以 32 或 64。一般来说,64 位数字没有完美的散列,XOR 方法比较普遍。例如,Java uses it to compute the hash of Long
  • 不,我最初以为您分配了 4*16 位,但您似乎只使用了较低的 4*13 位。花色转换并不准确,但 0x01 (card#0) 和 0x100000000 (card#32) 仍然会产生相同的位掩码。
  • @wildplasser 没错,有 2^52 手可能会因为从一个数字中丢掉 20 位而造成一些损失。了解游戏的细节将有助于改善这种“混叠”的效果——例如,知道一手牌不能超过 4 张牌或类似的东西会让我们为这手牌选择更好的表示。不过,一般来说,位掩码和 XOR 足以满足大多数实际用途(我在 iOS 应用程序中使用这种方法)。
  • @netanalyzer 有一种方法可以避免排序 - 请查看编辑。
【解决方案2】:

另一种方法是计算每个字符的出现次数,然后对结果向量进行哈希处理(向量count,其中count[c] 是字符c 的出现次数)。我不会说它比排序更好(字符数是固定的(并且可能非常低),因此您可以使用基数排序)(但我也不能说它更糟)。两者的时间复杂度:使用基数排序和计算每个字符的出现次数是线性的(此外,基数排序和计算字符几乎是一回事),所以这两者之间应该没有太大区别。

【讨论】:

    猜你喜欢
    • 2015-03-09
    • 2015-06-22
    • 1970-01-01
    相关资源
    最近更新 更多