在 C++ 中实现等价关系(使用 boost::disjoint_sets)
假设您有很多元素,并且需要跟踪它们之间的等价关系。如果元素A等价于元素B,则它等价于B所等价的所有其他元素。
我正在寻找一种有效的数据结构来编码这些信息。应该可以通过与现有元素的等价来动态添加新元素,并且根据该信息应该可以有效地计算新元素等价的所有元素。
例如,考虑以下元素 [0,1,2,3,4] 的等价集:
0 = 1 = 2
3 = 4
其中等号表示等价。现在我们添加一个新元素 5
0 = 1 = 2
3 = 4
5
并强制执行等价 5=3
,数据结构变为
0 = 1 = 2
3 = 4 = 5
由此,人们应该能够有效地迭代任何元素的等价集。对于 5,该集合将为 [3,4,5]。
Boost 已经提供了一个名为 disjoint_sets
的便捷数据结构,它似乎可以满足我的大部分要求。考虑这个简单的程序,它说明了如何实现上述示例:
#include <cstdio>
#include <vector>
#include <boost/pending/disjoint_sets.hpp>
#include <boost/unordered/unordered_set.hpp>
/*
Equivalence relations
0 = 1 = 2
3 = 4
*/
int main(int , char* [])
{
typedef std::vector<int> VecInt;
typedef boost::unordered_set<int> SetInt;
VecInt rank (100);
VecInt parent (100);
boost::disjoint_sets<int*,int*> ds(&rank[0], &parent[0]);
SetInt elements;
for (int i=0; i<5; ++i) {
ds.make_set(i);
elements.insert(i);
}
ds.union_set(0,1);
ds.union_set(1,2);
ds.union_set(3,4);
printf("Number of sets:\n\t%d\n", (int)ds.count_sets(elements.begin(), elements.end()));
// normalize set so that parent is always the smallest number
ds.normalize_sets(elements.begin(), elements.end());
for (SetInt::const_iterator i = elements.begin(); i != elements.end(); ++i) {
printf("%d %d\n", *i, ds.find_set(*i));
}
return 0;
}
如上所示,可以有效地添加元素,并动态扩展不相交的集合。如何有效地迭代单个不相交集合的元素,而不必迭代所有元素?
Assume you have many elements, and you need to keep track of the equivalence relations between them. If element A is equivalent to element B, it is equivalent to all the other elements B is equivalent to.
I am looking for an efficient data structure to encode this information. It should be possible to dynamically add new elements through an equivalence with an existing element, and from that information it should be possible to efficiently compute all the elements the new element is equivalent to.
For example, consider the following equivalence sets of the elements [0,1,2,3,4]:
0 = 1 = 2
3 = 4
where the equality sign denotes equivalence. Now we add a new element 5
0 = 1 = 2
3 = 4
5
and enforcing the equivalence 5=3
, the data structure becomes
0 = 1 = 2
3 = 4 = 5
From this, one should be able to iterate efficiently through the equivalence set for any element. For 5, this set would be [3,4,5].
Boost already provides a convenient data structure called disjoint_sets
that seems to meet most of my requirements. Consider this simple program that illustates how to implement the above example:
#include <cstdio>
#include <vector>
#include <boost/pending/disjoint_sets.hpp>
#include <boost/unordered/unordered_set.hpp>
/*
Equivalence relations
0 = 1 = 2
3 = 4
*/
int main(int , char* [])
{
typedef std::vector<int> VecInt;
typedef boost::unordered_set<int> SetInt;
VecInt rank (100);
VecInt parent (100);
boost::disjoint_sets<int*,int*> ds(&rank[0], &parent[0]);
SetInt elements;
for (int i=0; i<5; ++i) {
ds.make_set(i);
elements.insert(i);
}
ds.union_set(0,1);
ds.union_set(1,2);
ds.union_set(3,4);
printf("Number of sets:\n\t%d\n", (int)ds.count_sets(elements.begin(), elements.end()));
// normalize set so that parent is always the smallest number
ds.normalize_sets(elements.begin(), elements.end());
for (SetInt::const_iterator i = elements.begin(); i != elements.end(); ++i) {
printf("%d %d\n", *i, ds.find_set(*i));
}
return 0;
}
As seen above one can efficiently add elements, and dynamically expand the disjoint sets. How can one efficiently iterate over the elements of a single disjoint set, without having to iterate over all the elements?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您很可能无法做到这一点,
disjoint_sets
不支持仅对一组进行迭代。无论如何,底层数据结构和算法都无法有效地完成此操作,即即使disjoint_sets
内置支持仅对一组进行迭代,那也会与迭代一样慢所有集合,并过滤掉错误的集合。Most probably you can't do that,
disjoint_sets
doesn't support iteration over one set only. The underlying data structure and algorithms wouldn't be able to do it efficiently anyway, i.e. even if there was support built in todisjoint_sets
for iteration over one set only, that would be just as slow as iterating over all sets, and filtering out wrong sets.要么我遗漏了一些东西,要么你忘了提及一些东西,或者你可能想得太多了;)
令人高兴的是,对等不是平等。对于 A & B 等效;他们只需要共享具有相同值的属性。这可以是标量甚至向量。无论如何,我认为您发布的要求可以通过使用 std 来实现: :multiset ,它是 std::multiset::equal_range() 成员函数。
在线演示
注意:C++类继承还提供了另一种不可变等价非常有用;)
Either I am missing something, you forgot to mention something, or maybe you were overthinking this ;)
Happily, equivalence is not equality. For A & B to be equivalent; they only need to share an attribute with the same value. this could be a scalar or even a vector. Anyway, I think your posted requirements can be achieved just using std::multiset and it's std::multiset::equal_range() member function.
online demo
NB: C++ class inheritance also provides another kind of immutable equivalence that can be quite useful ;)