是否有更好的实现来保持唯一整数对的计数

Is there a better implementation for keeping a count for unique integer pairs?

本文关键字:整数 唯一 更好 实现 是否      更新时间:2023-10-16

这是在C++中。我需要对每一对数字进行计数。这两个数字的类型是"int"。我对这两个数字进行排序,所以(n1-n2)对与(n2-n1)对相同。我使用std::unordereded_map作为容器。

我一直在使用股份有限公司Wolfram Research的Matthew Szudzik的优雅配对函数。在我的实现中,该函数为每对两个类型为"int"的数字提供了一个类型"long"的唯一数字(在我的机器上为64位)。我使用这个长度作为我的unrdered_map(std::unrdered.map)的密钥。有没有更好的方法来统计这样的配对?我所说的更好是指更快,如果可能的话,内存使用率更低。

此外,我不需要所有的长。尽管你可以假设这两个数字的范围可以达到32位的最大值,但我预计我的配对函数的最大可能值最多需要36位。如果没有别的,至少有没有一种方法可以只让36位作为无序映射的密钥?(某些其他数据类型)

我曾想过使用比特集,但我不太确定std::hash是否会为任何给定的36比特比特集生成一个唯一的密钥,该密钥可以用作unordered_map的密钥。

如果有任何想法、建议等,我将不胜感激。

首先,我认为你的假设是错误的。对于std::unordered_mapstd::unordered_set,散列不必是唯一的(例如,原则上对于std::string这样的数据类型来说,散列不可能是唯一的),2个不同的密钥生成相同散列值的概率应该很低。但若发生碰撞,那个就不是世界末日,只是访问速度会变慢。我会从2个数字生成32位哈希,如果你对典型值有想法,只需测试哈希冲突的概率,并相应地选择哈希函数。

要做到这一点,您应该在std::unordered_map中使用一对32位数字作为密钥,并提供适当的哈希函数。计算唯一的64位密钥并将其与哈希映射一起使用是有控制的,因为hash_map将计算该密钥的另一个哈希,所以可能会使其变慢。

关于36位密钥,这不是一个好主意,除非你有一个处理36位数据的特殊CPU。您的数据将在64位边界上对齐,您将没有任何节省内存的好处,否则您将受到未对齐数据访问的惩罚。在第一种情况下,您只需要额外的代码就可以从64位数据中获得36位(如果处理器支持的话)。在第二秒内,即使存在一些冲突,您的代码也将比32位哈希慢。

如果hash_map是一个瓶颈,您可以考虑使用不同的哈希图实现,如goog-sparsehash.sourceforge.net

仅凭我的两分钱,您在文章中获得的配对函数比您实际需要的要复杂得多。将2个32位UNISIGNED值唯一地映射到64是很容易的。以下操作可以做到这一点,甚至可以处理非配对状态,而不会对数学外围设备造成太大的影响(如果有的话)。

uint64_t map(uint32_t a, uint32_t b)
{
    uint64_t x = a+b;
    uint64_t y = abs((int32_t)(a-b));
    uint64_t ans = (x<<32)|(y);
    return ans;
}
void unwind(uint64_t map, uint32_t* a, uint32_t* b)
{
  uint64_t x = map>>32;
  uint64_t y = map&0xFFFFFFFFL;
  *a = (x+y)>>1;
  *b = (x-*a);
}

另一种选择:

uint64_t map(uint32_t a, uint32_t b)
{
  bool bb = a>b;
    uint64_t x = ((uint64_t)a)<<(32*(bb));
    uint64_t y = ((uint64_t)b)<<(32*!(bb));
    uint64_t ans = x|y;
    return ans;
}
void unwind(uint64_t map, uint32_t* a, uint32_t* b)
{
  *a = map>>32;
  *b = map&0xFFFFFFFF;
}

这是一把独特的钥匙。您可以很容易地将其修改为无序映射的哈希函数提供程序,尽管这是否比std::map快取决于您所获得的值的数量。

注意:如果值a+b>32位,则此操作将失败。