next_permutation()的并行代码

时间:2015-06-16 10:39:03

标签: c++ algorithm optimization openmp permutation

我想知道我是否可以使用OpenMP并行化此代码。 OpenMP会让代码运行得更快吗?有没有更好的方法来实现这一目标?

vector<int> t; // already initialized
do{
    // Something with current permutation

} while(next_permutation(t.begin(), t.end()));

我知道我可以轻松地并行化for指令,但在这里我有一个while (condition = true)

2 个答案:

答案 0 :(得分:2)

使用Finding n-th permutation without computing others获取k=i*n/counti0count的第k个排列,其中n是排列,i是一个索引,count是线程数。

这会为您提供count块或间隔。在并行的单独线程中每个时间间隔内迭代,在每个线程中重复调用next_permutation

答案 1 :(得分:1)

  1. next_permutation以字典顺序产生排列,这意味着所产生的排列的前缀也按字典顺序排列。换句话说,您可以通过单独处理每个可能的初始元素来进行非常粗略的并行化:

    // Assume that v is sorted (or sort it)
    // This `for` loop should be parallelized
    for (auto n = v.size(), i = 0; i < n; ++i) {
      // Make a copy of v with the element at 'it' rotated to the beginning
      auto vprime = v;
      std::rotate(vprime.begin(), vprime.begin() + i, vprime.begin() + i + 1);
      // The above guarantees that vprime[1:] is still sorted.
      // Since vprime[0] is constant, we only need to permute vprime[1:]
      while (std::next_permutation(vprime.begin() + 1, vprime.end()) {
        // do something with vprime
      }
    }
    

    以上假设每个排列的处理时间大致相同。如果具有一些初始元素的排列的处理时间与具有一些其他初始元素的排列的平均时间不同,则一些线程将在其他线程之前终止,从而降低并行化的有效性。您可以通过使用大于一个元素的前缀来缩小并行化块。

  2. 您真正想要的是生成从 n 元素的向量中提取的 k 元素集的所有排列。有 n !/( n - k )!这种排列很快就变成了一个非常大的数字。例如,如果 n 为15且 k 为10,则有10,897,286,400个排列。即使处理速度很快,处理所有这些也需要一段时间。因此,寻求一种并行工作的方法是正确的。

  3. 要查找k组合的所有排列,如果您有一个产生所有组合的库函数,则对每个可能的组合执行next_permutation是一种合理的方法。但请注意,next_combination的许多实现都经过优化,易于使用,而不是性能。在循环中高效执行next_combination需要持久状态,这可以从根本上降低搜索下一个组合的成本。

    另一种方法是使用next_partial_permutation的实现,它直接产生n个元素中k的下一个排列。一个简单的解决方案基于next_permutation,但由于对std::reverse的额外调用,这也是次优的。 (值得考虑一下为什么这个算法有效。一个提示:如果你按字典顺序颠倒一个序列的第一个排列,你就会得到字典上的最后一个排列。)(代码改编自N2639)

    template<typename BidiIt>
    bool next_partial_permutation(BidiIt first, BidiIt middle, BidiIt last) {
      std::reverse(middle, last);
      return std::next_permutation(first, last);
    }
    

    无论您如何计算部分排列,您都可以使用与上述相同的方法对算法进行并行化:通过前缀(或初始元素,如果处理时间不变)对排列进行块化并执行块平行。