列出n个对象的所有k-排列的有效方法,同时满足特定标准

时间:2018-04-08 00:17:21

标签: c++ algorithm optimization permutation

标准是允许最多一个空对象,每个对象只能重复一次。

到目前为止,这是我的尝试。

假设n = 3,k = 3.设0表示空对象。

一些可能的例子:

011 101 110 112
012 102 120 113
013 103 130 121
... ... ... ...
033 303 330 332

所以,我创建了一个{0,1,1,2,2,3,3}的“池”。通过使用逻辑向量的置换,将从池中选择三个对象 (例如,逻辑向量{0,1,0,0,0,1,1}从池中选择1,3,3)

然后将三个选定对象的所有排列添加到集合中。

然而......会有一些重复,因为{0,1,0,0,0,1,1}被认为等同于{0,0,1,0,0,1,1,}两人都会从泳池中选择1,3,3。

对于更高的n和k,此代码变得非常昂贵,例如当n = 8且k = 6时。是否有更有效的方法来执行此操作?

我的C ++代码:

set< vector<int> > generate_kperms ( int n, int k )
{

  set< vector<int> > kperms;

  // create vector of integers { 0, 1, 1, 2, 2, ..., n, n }
  vector<int> pool( 2*n + 1 );
  pool[0] = 0;
  for ( int i = 1; i <= n; ++i )
    pool[2*i-1] = pool[2*i] = i;

  // create logical vector with k true values, to be permuted
  vector<bool> logical( pool.size() );
  fill( logical.end()-k, logical.end(), true );

  do {
    vector<int> kperm( k );
    vector<int>::iterator itr = kperm.begin();
    for ( int idx = 0; idx < (int) pool.size(); ++idx ) {
      if ( logical[idx] )
        *(itr++) = pool[idx];
    }
    do {
      kperms.insert( kperm );
    } while ( next_permutation ( kperm.begin(), kperm.end() ) );
  } while ( next_permutation( logical.begin(), logical.end() ) );

  return kperms;

}       /* -----  end of function generate_kperms  ----- */

1 个答案:

答案 0 :(得分:2)

观察一下,如果你生成pool的所有排列,那么长度为k的前缀几乎就是你想要的,只需要大量的连续重复。生成所有k-排列的一种简单但不错的方法是通过在调用next_permutation之前将n-k后缀排序为降序来跳过重复项。好的,

#include <iostream>
#include <set>
#include <vector>

using std::cout;
using std::greater;
using std::sort;
using std::vector;

vector<vector<int>> generate_kperms(int n, int k) {
  vector<vector<int>> kperms;
  vector<int> pool(2 * n + 1);
  pool[0] = 0;
  for (int i = 1; i <= n; ++i) {
    pool[2 * i - 1] = pool[2 * i] = i;
  }
  do {
    kperms.push_back(vector<int>(pool.begin(), pool.begin() + k));
    sort(pool.begin() + k, pool.end(), greater<int>());
  } while (next_permutation(pool.begin(), pool.end()));
  return kperms;
}

int main() {
  for (const vector<int> &kperm : generate_kperms(8, 6)) {
    for (int x : kperm) {
      cout << x << ' ';
    }
    cout << '\n';
  }
}

您可以通过实现自己的next_permutation版本来提高速度,该版本将n - k后缀视为反向排序,但我现在似乎无法在Knuth 4A中找到它。