in C++,循环遍历向量的所有 k 个元素

huangapple go评论60阅读模式
英文:

in C++, looping through all k elements of a vector

问题

在C++中,我有一个存储n位整数的int类型的vector。我想知道循环遍历该向量中所有不同的k个元素的最有效方法。也就是说,我想要循环遍历该向量中所有k个元素的组合。什么是最好的方法?

当k固定已知时,一种方法是使用嵌套的for循环,如下所示,但这种方法不是特别好,对于更大的k不可扩展,而且可能不是最有效的方法。

例1,k=2,使用两个嵌套的for循环。

#include <iostream>
#include <vector>

using namespace std;

int main() {
   vector<int> vect={1,8,9,10};
   for (int i:vect){
      for (int j:vect){
         //执行某些操作;
      }
   }
   return 0;
}

例2:利用元素是不同的事实,我可以通过索引循环,当向量很大时可以加快速度。

#include <iostream>
#include <vector>

using namespace std;

int main() {
   vector<int> vect={1,8,9,10};
   size_t vect_size = vect.size();
   for (size_t a = 0; a != vect_size ; a++) {
      for (size_t b = a+1; b != vect_size ; b++) {
         i = vect[a];
         j = vect[b];
         //执行某些操作
      }
   }
   return 0;
}

我尝试使用itertools库(从Python移植到C++),但它在我的笔记本电脑上无法运行,还不清楚原因。所以我想将这种类型的代码扩展到任何k。

英文:

In C++, I have a vector of int, such that each element is a n-bits integer.

I would like to know the most efficient way to loop through all distinct k elements of the vector. i.e. I want to loop through all combinations of k elements of my vector. What is the 'best' way to do it ?

When k is fixed and know, one way to do it is with nested for loops, as below, but the approach is not really nice, not scalable to higher k, and probably not the most efficient.

Example1 with k=2, using two nested for loops.

#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

int main() {
   vector&lt;int&gt; vect={1,8,9,10};
   for (int i:vect){
      for (int j:vect){
         //do something;
      }
   }
   return 0;
}

Example2: Using the fact that the element are distinct, I can loop through indices, and make it much faster when the vector is large.

#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

int main() {
   vector&lt;int&gt; vect={1,8,9,10};
   size_t vect_size = vect.size();
   for (size_t a = 0; a != vect_size ; a++) {
      for (size_t b = a+1; b != vect_size ; b++) {
         i = vect[a];
         j = vect[b];
         //do something
      }
   }
   return 0;
}

I tried to use the itertools package (ported from python to C++), but it's not working on my laptop, not sure why yet. So I would like to scale that type of code to any k.

答案1

得分: 2

以下是代码部分的翻译:

解决这个问题的一种更或多的标准方法是使用选择器并在选择器上进行排列。

示例:假设您有一个带有五个元素的 `std::vector`。然后您需要创建一个辅助的 `std::vector&lt;bool&gt; selector` 并将 k 个元素设置为 true。对选择器进行排列,然后将数据复制,将“true”对应的选择器复制到结果中:

5 个值:k= 2

值       1 2 3 4 5
选择器   0 0 0 1 1

存储选择的数据 4 和 5。
获取选择器的下一个排列

值       1 2 3 4 5
选择器   0 0 1 0 1

存储选择的数据 3 和 5。
获取选择器的下一个排列

值       1 2 3 4 5
选择器   0 0 1 1 0

存储选择的数据 3 和 4。
获取选择器的下一个排列

值       1 2 3 4 5
选择器   0 1 0 0 1

存储选择的数据 2 和 5。
获取选择器的下一个排列 . . .

以此类推 . . .

还有一个附加要求,即要获得不同的结果。我们将使用 `std::set` 来实现这一点,它只能包含唯一的值。

这个实现非常直观和易于理解。请参阅以下示例:

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;set&gt;
#include &lt;iterator&gt;

std::set&lt;std::set&lt;int&gt;&gt; getCombinations(const std::vector&lt;int&gt;&amp; v, int k) {

	std::set&lt;std::set&lt;int&gt;&gt; result{};

	// 创建选择器数组
	std::vector&lt;bool&gt; selector(v.size(), false);

	// 将选择器数组中的 k 个元素设置为 true
	std::fill(selector.begin(), std::next(selector.begin(), k), true);

	// 现在我们将对选择器进行所有的排列
	do {
		// 临时存储一个组选择的数据
		std::set&lt;int&gt; group{};

		// 如果相应的选择器被设置,将数据复制到组中
		std::copy_if(v.begin(), v.end(), std::inserter(group,group.begin()), [&amp;, k = 0u](int i)mutable { return selector[k++]; });

		// 仅存储具有所需大小 k 的数据
		if (group.size() == k)
			result.insert(group);

	} while (std::prev_permutation(selector.begin(), selector.end()));

	return result;
}

// 对所有可能的 k 进行测试的代码
int main() {
	std::vector data{ 1,2,3,3,3,4,5 };
	
	for (int k = 1; k &lt;= data.size(); ++k) {
		auto combinations = getCombinations(data, k);
		std::cout &lt;&lt; "n\nFor k = " &lt;&lt; k &lt;&lt; "n";
		for (const auto&amp; v : combinations) {
			for (const int i : v) std::cout &lt;&lt; i &lt;&lt; ' ';
			std::cout &lt;&lt; 'n';
		}
	}
}

结果:

For k = 1
1
2
3
4
5


For k = 2
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5


For k = 3
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5


For k = 4
1 2 3 4
1 2 3 5
1 2 4 5
1 3 4 5
2 3 4 5


For k = 5
1 2 3 4 5


For k = 6


For k = 7

如果您还希望有重复的值,那么可以将 std::set 替换为 std::vector

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;set&gt;
#include &lt;iterator&gt;

std::set&lt;std::vector&lt;int&gt;&gt; getCombinations(const std::vector&lt;int&gt;&amp; v, int k) {

	std::set&lt;std::vector&lt;int&gt;&gt; result{};

	// 创建选择器数组
	std::vector&lt;bool&gt; selector(v.size(), false);

	// 将选择器数组中的 k 个元素设置为 true
	std::fill(selector.begin(), std::next(selector.begin(), k), true);

	// 现在我们将对选择器进行所有的排列
	do {
		// 临时存储一个组选择的数据
		std::vector&lt;int&gt; group{};

		// 如果相应的选择器被设置,将数据复制到组中
		std::copy_if(v.begin(), v.end(), std::back_inserter(group), [&amp;, k = 0u](int i)mutable { return selector[k++]; });

		// 存储具有所需大小 k 的数据
		result.insert(group);

	} while (std::prev_permutation(selector.begin(), selector.end()));

	return result;
}

结果:

For k = 1
1
2
3
4
5


For k = 2
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 3
3 4
3 5
4 5


For k = 3
1 2 3
1 2 4
1 2 5
1 3 3
1 3 4
1 3 5
1 4 5
2 3 3
2 3 4
2

<details>
<summary>英文:</summary>

The more or less standard approach to solve this problem is to use a selector and permute over the selector.

Example: Assume, you have a `std::vector` with five elements. Then you need to create a helper `std::vector&lt;bool&gt; selector` and set k elements to true. Do a permutation over the selector and then copy the data, with a &quot;true&quot; corresponding selector into the result:

```TXT
5 Values: k= 2

Values    1 2 3 4 5
Selector  0 0 0 1 1

Store selected data 4 and 5. 
Get next permutation for selector

Values    1 2 3 4 5
Selector  0 0 1 0 1

Store selected data 3 and 5. 
Get next permutation for selector

Values    1 2 3 4 5
Selector  0 0 1 1 0

Store selected data 3 and 4. 
Get next permutation for selector

Values    1 2 3 4 5
Selector  0 1 0 0 1

Store selected data 2 and 5. 
Get next permutation for selector . . .

And so on and so on . . .

An additional requirement was to have distinct results. We will achieve this by using a std::set which can hold only unique values.

The implementation is the straight forward and easy to understand. Please see the below example:

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;set&gt;
#include &lt;iterator&gt;

std::set&lt;std::set&lt;int&gt;&gt; getCombinations(const std::vector&lt;int&gt;&amp; v, int k) {

	std::set&lt;std::set&lt;int&gt;&gt; result{};

	// Create the selector array
	std::vector&lt;bool&gt; selector(v.size(), false);

	// Set k elements in the selector array to true
	std::fill(selector.begin(), std::next(selector.begin(), k), true);

	// Now we will do all permutations of the selector
	do {
		// Temporary, to store one group of selected data
		std::set&lt;int&gt; group{};

		// Copy data into group, if corresponding selector is set
		std::copy_if(v.begin(), v.end(), std::inserter(group,group.begin()), [&amp;, k = 0u](int i)mutable { return selector[k++]; });

		// Only store data, with the requested size of k
		if (group.size() == k)
			result.insert(group);

	} while (std::prev_permutation(selector.begin(), selector.end()));

	return result;
}

// Test code for all possible ks
int main() {
	std::vector data{ 1,2,3,3,3,4,5 };
	
	for (int k = 1; k &lt;= data.size(); ++k) {
		auto combinations = getCombinations(data, k);
		std::cout &lt;&lt; &quot;\n\nFor k = &quot; &lt;&lt; k &lt;&lt; &quot;\n&quot;;
		for (const auto&amp; v : combinations) {
			for (const int i : v) std::cout &lt;&lt; i &lt;&lt; &#39; &#39;;
			std::cout &lt;&lt; &#39;\n&#39;;
		}
	}
}

Result:

For k = 1
1
2
3
4
5


For k = 2
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5


For k = 3
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5


For k = 4
1 2 3 4
1 2 3 5
1 2 4 5
1 3 4 5
2 3 4 5


For k = 5
1 2 3 4 5


For k = 6


For k = 7

IF you also want to have duplicated vales, then replace the std::set with a std::vector

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;set&gt;
#include &lt;iterator&gt;

std::set&lt;std::vector&lt;int&gt;&gt; getCombinations(const std::vector&lt;int&gt;&amp; v, int k) {

	std::set&lt;std::vector&lt;int&gt;&gt; result{};

	// Create the selector array
	std::vector&lt;bool&gt; selector(v.size(), false);

	// Set k elements in the selector array to true
	std::fill(selector.begin(), std::next(selector.begin(), k), true);

	// Now we will do all permutations of the selector
	do {
		// Temporary, to store one group of selected data
		std::vector&lt;int&gt; group{};

		// Copy data into group, if corresponding selector is set
		std::copy_if(v.begin(), v.end(), std::back_inserter(group), [&amp;, k = 0u](int i)mutable { return selector[k++]; });

		// store data, with the requested size of k
		result.insert(group);

	} while (std::prev_permutation(selector.begin(), selector.end()));

	return result;
}

Result:

For k = 1
1
2
3
4
5
For k = 2
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 3
3 4
3 5
4 5
For k = 3
1 2 3
1 2 4
1 2 5
1 3 3
1 3 4
1 3 5
1 4 5
2 3 3
2 3 4
2 3 5
2 4 5
3 3 3
3 3 4
3 3 5
3 4 5
For k = 4
1 2 3 3
1 2 3 4
1 2 3 5
1 2 4 5
1 3 3 3
1 3 3 4
1 3 3 5
1 3 4 5
2 3 3 3
2 3 3 4
2 3 3 5
2 3 4 5
3 3 3 4
3 3 3 5
3 3 4 5
For k = 5
1 2 3 3 3
1 2 3 3 4
1 2 3 3 5
1 2 3 4 5
1 3 3 3 4
1 3 3 3 5
1 3 3 4 5
2 3 3 3 4
2 3 3 3 5
2 3 3 4 5
3 3 3 4 5
For k = 6
1 2 3 3 3 4
1 2 3 3 3 5
1 2 3 3 4 5
1 3 3 3 4 5
2 3 3 3 4 5
For k = 7
1 2 3 3 3 4 5

答案2

得分: -1

这将适用于所有k的值:

  • 使用大小为kstd::vector<size_t> index
  • 用不同的索引0,1,2,3...来初始化它
  • 将其视为基数为vect.size()的数字,也就是通过增加最后一个元素来获得下一个索引。当元素达到vect.size()时,将进位到下一个“数字”。

这实际上与使用k位在基数为vect.size()中计数是一样的。在每次迭代中,所需的组合是:

for (auto i : index) {
std::cout << vect[i] << " ";
}
英文:

This will work for all values of k:

  • use a std::vector&lt;size_t&gt; index of size k
  • initialize it with distinct indices 0,1,2,3...
  • treat it as a base-vect.size() number, ie you get the next index by incrementing the last element. When the element reaches vect.size() you carry over to the next "digit".

It is really the same as counting in base vect.size() with k digits. In each iteration the desired combination is

 for (auto i : index) { 
std::cout &lt;&lt; vect[i] &lt;&lt; &quot; &quot;;
}

huangapple
  • 本文由 发表于 2023年2月24日 15:54:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75553885.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定