如何快速检查(非平凡)数字列表的等价性?

时间:2011-02-25 00:16:19

标签: c++ algorithm optimization data-structures set

我有一个整数列表,例如1,2,2,3,4,1。我需要能够检查不同列表之间的等价性(==)。

但是,我并不是指一个简单的数字比较。这些列表中的每一个实际上表示集合分区,其中列表中的位置表示元素的索引,而数字表示组的索引。例如,在前者中,元素0和元素5在同一组中,元素1和2在同一组中,元素3和4都在它们各自的组中。 该组的实际索引并不重要,只有分组。

我需要能够在这个意义上测试等价,所以例如前面的列表相当于5,3,3,2,9,5,,因为它们具有相同的分组。

我这样做的方法是将数组缩减为一种正常形式。我发现所有数字都与第一个数字具有相同的值,并将这些全部设置为0。 然后我继续在列表中找到一个新的数字,找到相同值的所有数字,并将它们全部设置为1.我继续这样。

在我的示例中,两个数字都会减少到会减少到0,1,1,2,3,0,当然我可以只使用一个简单的比较来查看它们是否相同。

然而这很慢,因为我必须在列表上进行几次线性传递。那么为了减少追逐,是否有更有效的方法将这些数字减少到这种正常形式?

然而,更一般地说,我可以一起避免这种减少,并以不同的,可能更有效的方式比较数组吗?

实施细节

  • 实际上是实现了这些数组 作为节省空间的位集,所以我每次都必须遍历整个列表,因为没有rb_tree esque哈希继续进行。
  • 大 这些数组的数量将是 因此,存储在stl unordered_set中 应考虑哈希的要求

4 个答案:

答案 0 :(得分:18)

尝试并行迭代这两个序列,将第一个数组中的值(std::map或数组)保持为第二个数组中的值,反之亦然。如果你找到一个不在你表中的一对,那么添加它,除非表中有第一个或第二个数字的东西(因为这表明不平等)。例如:

1,2,2,3,4,1
5,3,3,2,9,5

您将添加1-> 5,2-> 3,3-> 2和4-> 9,并且比较将通过。对于略有不同的东西:

5,3,3,2,9,5
1,2,2,3,2,1

你会添加5-> 1,3-> 2,2-> 3,然后9-> 2会失败,因为在第二个序列中已经存在2的结合;因此,你会知道序列不相等。

对于创建哈希函数,您可能需要进行正在进行的规范化,但它只需要一次遍历序列。同样,保持两个方向的地图,但如果在输入序列中找到未知元素,则将其映射到下一个可用数字,否则使用地图将输入序列转换为标准化序列。

答案 1 :(得分:2)

对于K符号的字母和这些符号的N数组,您应该能够生成签名(或规范表示O(N)中数组的em>),使用哈希表,或O(N log K)使用二叉搜索树。

诀窍是在一次传递中执行所有数字的转换:

std::unordered_map<std::size_t,std::size_t> map;

std::vector<std::size_t> signature;
signature.reserve(array.size());

for (std::size_t i: array) {
  // insert only inserts if they key is not already present
  // it returns std::pair<iterator,bool> with iterator pointing
  // to the pair {key: i, value: index}
  size_t index = map.insert({i, map.size()}).first->second;
  signature.push_back(index);
}

然后,数组的散列是其签名的散列。

但更重要的是,没有理由不将所有数组一次性放入规范表示

答案 2 :(得分:1)

您可以覆盖哈希算法并创建一个唯一编码分组的哈希键。这样,当每个数组插入到散列表中时,具有相同组编码的所有数组将被链接到相同的散列位置。插入所有数组后,阵列将已经分组。

可能的编码可能是:[1 2 2 3 4 1]将散列到162345.(嗯...抱歉编码是非唯一的)。

我们需要一个独特的编码,因此我们需要记录数组中分组的位置和计数。那怎么样呢?

  

[1 2 2 3 4 1] - &gt; 1622324151(来自   从左到右,随后是群体指标   通过设置基数)

     

[5 5 5 9 9 9] - &gt; 12334563

     

[1 2 3 4 5 6] - &gt; 112131415161

我确信有更聪明的编码,但这将是一个非常快速的哈希。

答案 3 :(得分:0)

如果您知道最大可能的“组”,那么您可以执行类似的操作(伪代码,但您应该能够弄明白:)

for i = 0; i < listLength; i++
    if !mapping[list1[i]]
        mapping[list1[i]] = list2[i]
    if mapping[list1[i]] != list2[i]
        return false;
return true;