确保集合新颖性的有效方法

Posted

技术标签:

【中文标题】确保集合新颖性的有效方法【英文标题】:Efficient way of ensuring newness of a set 【发布时间】:2018-09-30 04:33:55 【问题描述】:

给定集合N = 1,...,n,考虑P 的不同预先存在的N 子集。子集S_p 的特征在于 0-1 n 向量 x_p 其中 ith 元素是 0 或 1,具体取决于 ith(n)项是否属于子集与否。让我们称之为x_ps 指标向量

例如,如果N=1,2,3,4,5,则子集1,2,5由向量(1,0,0,1,1)表示。

现在,给定 P 预先存在的子集及其关联向量 x_ps。

计算由向量y表示的候选子集​​。

检查y 是否已经是P 预先存在的子集的一部分或者y 是否确实是一个新的子集而不是P 子集的一部分的最有效方法是什么?

以下是我能想到的方法:

(方法1)基本上,我们必须对所有预先存在的集合进行逐个元素的检查。伪代码如下:

for(int p = 0; p < P; p++)
     //(check if x_p == y by doing an element by element comparison)
     int i;
     for(i = 0; i < n; i++)
         if(x_pi != y_i)
             i = 999999;
                      
     
     if(i < 999999)
          return that y is pre-existing


return that y is new

(方法 2)想到的另一个想法是存储指标向量 x_ps 的十进制等效值(其中指标向量被视为二进制表示)并将其与 y 的十进制等效值进行比较.也就是说,如果一组P 预先存在的集合是: (0,1,0,0,1), (1,0,1,1,0) ,则该集合的存储小数将为9, 22。如果y(0,1,1,0,0),我们计算12 并对照集合9, 22 进行检查。这种方法的好处是,对于每个新的y,我们不必检查每个预先存在的集合的n 元素。我们可以只比较十进制数。

问题1。在我看来(方法2)应该比(方法1)更有效。对于(方法2),是否有一种有效的方法(C/C++ 中的内置库函数)将x_ps 和y 从二进制转换为十进制?这些指标变量的数据类型应该是什么?例如,bool y[5];char y[5];

问题2.有没有比(方法2)更有效的方法?

【问题讨论】:

n的最大值是多少? 看看Bloom filter 是否有帮助。 @user3386109 高达 1000 如果 n 是 1000,那么十进制等价物是 1000 位数字。所以是一个由 16 个 64 位数字或 32 个 32 位数字组成的数组。 哦,还不错。在 C++ 中,您可以将 x_p 向量转换为 strings 并将字符串存储在 std::set 中。 【参考方案1】:

您已经注意到,指标向量和 N 位整数之间存在微不足道的同构。这意味着您的问题 2 的答案是“否”:可用于维护集合和测试其中成员资格的工具与整数相同(哈希表带来了正常的方法)。评论中提到了 Bloom 填充器,它可以有效地测试成员资格,但存在一些误报的风险,但 Bloom 过滤器通常适用于比您看到的更大的数据量。

至于你的问题1:方法2是合理的,比你想象的还要容易。虽然vector&lt;bool&gt; 没有给您提供将其转换为整数块的简单方法,但在实现上我知道它已经以这种方式实现(C++ 标准允许对该特定向量类型进行特殊处理,这在当今普遍被考虑这是一个糟糕的决定,但偶尔会产生一些好处)。这些向量是可散列的。因此,只需保持unordered_set&lt;vector&lt;bool&gt;&gt; 左右,您将获得相当接近最佳性能的性能。 (如果你在编译时知道N,你可能更喜欢bitset 而不是vector&lt;bool&gt;。)

【讨论】:

所以,如果我理解正确的话,我可以有std::vector&lt;std::vector&lt;bool&gt; &gt; preexistingsets;std::vector&lt;bool&gt; candidateset; 然后,要检查candidateset 是否预先存在,我应该只做一个for 循环,遍历每个预先存在的逐个元素设置和检查?或者,有没有我可以使用的库函数?我在编译时确实知道 N 。所以,我也会看看bitset 我想我理解unordered_set。所以,我可以拥有unordered_set&lt;bitset &lt;N&gt;&gt; preexistingsets;。让我做实验。 @Tryer 您可以直接将两个vector&lt;bool&gt;s 与== 进行比较。操作员将负责循环遍历向量的内容,希望优化以比较字节大小或更大的块,而不是逐位比较。 bitset 也一样。您无需编写自己的每个元素的比较。 @Sneftel 我有以下内容:std::unordered_set&lt;std::bitset&lt;1000&gt;&gt; S; 假设我想将指标向量10011000...0 添加到S 中。我应该先做一个 std::bitset&lt;1000&gt; bset; 然后是适当的 bset.set(index); 然后最后是 S.insert(bset); 吗?那是最有效的吗?或者是否有类似 emplace_back 的东西或类似的东西可以帮助将 bitset 直接构建到 unordered_set 中,而不是先在外部创建它然后将其复制到容器中? 只需构建位集,然后将其插入。这不会成为您的性能瓶颈。【参考方案2】:

方法 2 可以通过计算给定子集的十进制等效值并使用模数 1e9+7 对其进行散列来优化。这会导致每次 N

#define M 1000000007  //big prime number
unordered_set<long long> subset;  //containing decimal representation of all the 
                                  //previous found subsets

/*fast computation of power of 2*/
long long Pow(long long num,long long pow)
    long long result=1;
    while(pow)
    
        if(pow&1)
        
            result*=num;
            result%=M;
        
        num*=num;
        num%=M;
        pow>>=1;
    
    return result;

/*checks if subset pre exists*/
bool check(vector<bool> booleanVector)
    long long result=0;
    for(int i=0;i<booleanVector.size();i++)
        if(booleanVector[i])
            result+=Pow(2,i);
    return (subset.find(result)==subset.end());

【讨论】:

2^1000 &gt; 1e9+7(相差很大)。根据鸽巢原理,您不能使用模数(或其他散列方法)来减少范围而不引起冲突。 最后,您在循环中使用Pow 来计算与指标向量对应的整数的方法效率低下:最好通过移位和通过平方添加而不是使用求幂(O(n)O(n log n))。 我已经尝试并测试了使用 M = 1e9+7 的代码,并且在 5e6 之前没有发生冲突,正如代码中所做的那样。 code。此外,您的第二条评论仅删除了 log(N) 因子,这与 N 碰撞不太可能。但是您不能仅根据一些测试就声称它们是不可能的。如果你稍微研究一下,你会发现一对具有相同哈希的位向量。 确实,根据您的评论,您确实找到了一个。那么,鉴于存在碰撞,是什么阻止了前两个位向量成为那对?

以上是关于确保集合新颖性的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

确保Java中顺序命令的最有效方法

从其他集合构建集合的有效方法

SQL - 确保在一组关键密钥对中表示的两个实体都存在于最终数据集中的有效方法

6个实用方法有效确保WordPress网站数据安全

采用基于集合的机器学习方法,进行有效的历史拟合

从集合中确定最近位置的快速/有效方法