【问题标题】:What is a fast data-structure to implement a pair storage, as specified here?什么是实现对存储的快速数据结构,如此处所述?
【发布时间】:2014-05-30 06:30:37
【问题描述】:

我正在尝试实现值对的存储。它必须公开一个简单的 API:newPair, get, remove, isMember。一个值可以由4-bit int 或指向另一对的指针组成。所以,例如:

Pointer a = API.newPair(13,12);
Pointer b = API.newPair(a,7);
Pointer c = API.newPair(2,b);
Pointer d = API.newPair(a,7);

等等。但是,只有一个警告:绝对不能将一对存储两次。如果您尝试添加一个已经存在的对,它只会返回现有的指针。因此,例如,在上面的代码中,b == d

我的幼稚实现涉及一堆对。每次添加一个新对时,我只是迭代堆栈,如果我在那里找到这对,我返回它的位置。如果没有,我在堆栈中添加了一个新元素并返回堆栈大小。现在我需要让它尽可能快。我目前的想法是只使用哈希表而不是堆栈。

那么,问题是:什么是实现这一点的快速方法?

【问题讨论】:

  • 可能是一个哈希表,例如std::unoedered_set<YourPairType>。你只需要实现一个哈希函数和一个相等比较。

标签: c++ algorithm math data-structures hash


【解决方案1】:

一个双射 NxN -> N 由Cantor Pairing Function 给出

如果 N 是无限集,则可以使用

。它通过将 NxN 的元素连续分配给 N 来工作:

当 x,y 是非负整数时,可以使用 ElegantPairing 中引入的映射

x >= y ? x * x + x + y : x + y * y;  where x, y >= 0

通过沿正方形的边缘分配值来工作。

代码

int cantor_pairing( int k1, int k2) {
    return 0.5 * ( k1 + k2) * ( k1 + k2 + 1) + k2;
}

int szuzik_pairing( int x, int y) {
    if( x < 0 || y < 0) return -1;
    return x >= y ? x * x + x + y : x + y * y;
}

即使你的整数是联合均匀分布的,你也不能只使用映射

(x,y) -> x

即完全忽略了他们中的第二个。这是因为在正方形 NxN 上,f(x,y) 将为 1/N^2,但边际分布 f_y(x) = 1/N,这将是发生碰撞的概率。 毕竟,很可能情况并非如此,因为用户会更频繁地选择小数字而不是大数字。

【讨论】:

  • 哇,这个答案太准确了,我什至无法回答。你在看我的其他问题吗?
  • 我只是在看“什么是整数对的快速哈希函数?”
  • 之前的问题是一个完全不相关的问题,关于 godel 编号和上下文无关语法的枚举。我只是发现你实际上也给了我关于这个问题的一个很好的参考,这让我很惊讶。
  • Viclib 我已经阅读了“什么是整数对的快速散列函数?”这个问题。大约 30 分钟前由您自己发布的
【解决方案2】:

如果您的对具有 4 位元素,则一个 char(在 99% 的情况下为 8 位长)就足以存储一对。一点评论:使用 4 位长(有符号)整数,您的允许值范围是:[-7, 8],但是,在您的示例中,您使用了值 12 和 13,所以,我理解您的对由两个 4 位无符号整数组成。

// a and b should be values between 0 and 15.
// Otherwise, the behaviour is undefined
// (or if you want, you could control the values of
//  a/b and throw an exception in the opposite case).
Pair APIClass::newPair(unsigned a, unsigned b)
{
    char c = (char(a) << 4) | char(b);

    return Pair(c); // `typedef char Pair;` p.e. 
}

假设unsigneds ab 在0到15之间,二进制:

a ≡ 0000000000000000000000000000xxxx // 32-bits
b ≡ 0000000000000000000000000000yyyy // 32-bits

目标是:

Pair ≡ xxxxyyyy // 8-bits

因此,发生了以下转换:

// For a
0000000000000000000000000000xxxx => conversion to char
0000xxxx                         => 4-bits left-shift
xxxx0000

// For b
0000000000000000000000000000yyyy => conversion to char
0000yyyy

// Result:

      xxxx0000
  |   0000yyyy
--------------
      xxxxyyyy

仅此而已。

最后,如果您的意思是 4 字节 ints,同样的推理也适用,但使用 long long int 作为占位符(尽管并非在每个架构中 long long int 都是 64 位长)。

【讨论】:

  • 这对于 4 位的情况来说很好,但如果它是指向另一对的指针呢?
  • @harold 复制其内容。 “不要存储两次”的条件只有在一对的存储明显大于指针时才有意义。在这种情况下,指针比char 大四倍。在这种情况下,使用辅助数据结构和开销进行搜索操作是没有逻辑的。
  • 将其内容复制到哪里?现在它是 12 位、16 位或 20 位等 - 显然你会在那里遇到麻烦。此外,节点的 DAG 可以比其树化版本小得多。
  • 不是一对4位整数或指针,而是一对(4位整数或指针)
  • @Viclib,好吧,你是对的。对于 4 位整数等要求,重复的概率很高。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-09
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多