使用STL列出特定子集

Listing specific subsets using STL

本文关键字:子集 STL 使用      更新时间:2023-10-16

假设我有一个数字范围,例如{2,3,4,5},以这种顺序存储在std::vector v中,并且我想使用STL列出以5结尾的所有可能的子集…即:

2 3 4 5
2 3 5
2 4 5
3 4 5
2 5
3 5
4 5
5

(我希望我没有忘记:))

我尝试使用while(next_permutation(v.begin(),v.end())),但没有得到想要的结果:)

有人有什么想法吗?

PS:那些做过google code jam 2010档案的人可能会认出这个:)

让我们关注打印所有子集的问题。如你所知,如果你有n个元素的向量,你就会有2^n个可能的子集。这不是巧合,如果您有n位整数,那么最大存储值是2^n。如果将每个整数视为位向量,则遍历所有可能的值将得到位的所有可能子集。通过迭代整数,我们可以免费得到子集!

假设vector的元素不超过32个(超过40亿个可能的子集!),这段代码将打印vector v的所有子集(不包括空子集):

for (uint32_t mask =1; mask < (1<<v.size()); ++mask)
{
  std::vector<int>::const_iterator it = v.begin();
  for (uint32_t m =mask; m; (m>>=1), ++it)
  {      
    if (m&1) std::cout << *it << " ";
  }
  std::cout << std::endl;
}

我只是为vector的大小创建了所有可能的位掩码,并遍历每个位;如果设置好了,就打印相应的元素。

现在应用以特定数字结束的规则是小菜一碟(通过在遍历掩码时检查附加条件)。最好是,如果vector中只有一个5,则可以将其交换到末尾并打印不含最后一个元素的vector的所有子集。

我有效地使用std::vector, const_iteratorstd::cout,所以你可能会认为它是使用STL解决的。如果我想出更STLish的东西,我会让你知道(好吧,但如何,它只是迭代)。您可以使用此函数作为STL解决方案的基准;-)

EDIT:正如Jørgen Fogh所指出的,如果你想在大向量上操作,它不能解决你的子集蓝调。实际上,如果您想打印32个元素的所有子集,它将生成数tb的数据。如果您觉得受32常量的限制,您可以使用64位整数,但您甚至不会结束对所有数字的迭代。如果您的问题只是回答有多少期望子集,那么您肯定需要另一种方法。STL也不会有多大帮助;-)

您可以使用任何容器,我将使用std::set,因为它紧邻我们想要表示的内容。现在你的任务是找出所有以5结尾的子集所以我们取初始集合并从中移除5。现在我们想要这个新集合的所有子集,并在它们后面加上5。

void subsets(std::set<std::set<int>> &sets, std::set<int> initial)
{
    if(initial.empty())
        return;
    sets.insert(initial);//save the current set in the set of sets
    std::set<int>::iterator i = initial.begin();
    for(; i != initial.end(); i++)//for each item in the set
    {
        std::set<int> new_set(initial);//copy the set
        new_set.erase(new_set.find(*i));//remove the current item
        subsets(sets, new_set);//recursion ...
    }
}

sets是包含您想要的所有子集的集合。initial是您希望拥有其子集的集合。最后用subsets(all_subsets, initial_list_without_5);

调用它

这将创建子集,最后您可以将5附加到所有子集。顺便说一句,别忘了空集合:)

还要注意,创建和删除所有这些集合的效率不是很高。如果你想让它更快,final set应该得到指向set的指针,new_set应该动态分配…

tomasz描述了一个只要n<=32就可以工作的解决方案,尽管打印2^32个不同的子集将花费非常很长的时间。由于大型数据集的边界是2 <= n <= 500,因此生成所有子集肯定不是可行的方法。你需要想出一些聪明的方法来避免生成它们。事实上,这就是问题的全部。

如果你愿意,你可以用谷歌搜索这个问题的答案。我的提示是,您需要查看集合的结构,并完全避免生成它们。

使用置换来创建向量的向量。然后使用std::partition和一个函数将其排序为以5结尾的向量和不以5结尾的向量。