如何在C ++中创建一种算法来查找集合的变体而无需重复(即n个元素,选择k)?

时间:2019-03-03 15:49:23

标签: c++ algorithm permutation

例如targets: function(i){return elements2[i];}, ,我已经设置了(n = 3, k = 2),我需要我的算法来查找: {1, 2, 3}

我能够使用{1, 2}, {1, 3}, {2, 1}, {2, 3}, {3, 1}, {3, 2}来创建算法,但是对于next_permutation来说,它的工作速度非常慢(这是我所需要的)。

这是我的代码:

n = 10, k = 4

我怎样才能使算法更快呢?

5 个答案:

答案 0 :(得分:6)

此代码按字典顺序从n生成k个项目的排列,为简单起见打包成整数(因此153对应于(1,5,3))

void GenArrangement(int n, int k, int idx, int used, int arran) {
    if (idx == k) {
        std::cout << arran << std::endl;
        return;
    }

    for (int i = 0; i < n; i++) 
        if (0 == (used & (1 << i))) 
            GenArrangement(n, k, idx + 1, used | (1 << i), arran * 10 + (i + 1));
}

int main()
{
    GenArrangement(5, 3, 0, 0, 0);
}

123 124 125 132 134 135 142 143 145 152 153 154 213 214 215 231 234 235 241 243 245 251 253 254 312 314 315 321 324 325 341 342 345 351 352 354 412 413 415 421 423 425 431 432 435 451 452 453 512 513 514 521 523 524 531 532 534 541 542 543

答案 1 :(得分:4)

您可以使用位掩码遍历每个子集。

for(unsigned int i = 0; i < (1<<10);i++)

当您不需要可移植代码时,可以使用

__builtin_popcount(int)

至少要在x86处理器的gcc中获得二进制表示形式中的1。

for(unsigned int i = 0; i < (1<<10);i++) {
    if(__builtin_popcount(i) == 4) { //Check if this subset contains exactly 4 elements
        std::string s;
        for(int j = 0; j < 10; j++) {
            if(i&(1<<j)) { //Check if the bit on the j`th is a one
                s.push_back(to_string(j));
            }
        }
        v.push_back(s);
    }
}

答案 2 :(得分:3)

缓慢是由于生成所有n!排列,即使只需要其中的一小部分。您的复杂度大约为O(n!* k log n),其中 O(k log n)是使用所有排列查询std::map的复杂度上限。 / p>

answer by MBo限制为9个值(1..9)。即使扩展到打印更长的值,它们也仍然受位数限制(对于int,通常为31,如果有uint64_t可用,则为64位)。

这里是:

void print_permutations_impl(std::ostream & out, std::vector<int> & values,
                             unsigned k, std::vector<int> & permutation_stack)
{
    if (k == permutation_stack.size())
    {
        const char* prefix = "";
        for (auto elem: permutation_stack) {
            out << prefix << elem;
            prefix = ", ";
        }
        out << '\n';
        return;
    }
    auto end_valid = values.size() - permutation_stack.size();
    permutation_stack.push_back(0);
    for (unsigned i=0 ; i < end_valid; ++i) {
        permutation_stack.back() = values[i];
        std::swap(values[i], values[end_valid - 1]);
        print_permutations_impl(out, values, k, permutation_stack);
        std::swap(values[i], values[end_valid - 1]);
    }
    permutation_stack.pop_back();
}

void print_permutations(std::ostream & out, const std::vector<int> & values, int k)
{
   std::vector<int> unique = values;
   std::sort(unique.begin(), unique.end());
   unique.erase(std::unique(unique.begin(), unique.end()),
                unique.end());
   std::vector<int> current_permutation;
   print_permutations_impl(out, unique, k, current_permutation);
}

在N = 100和K = 2的情况下,它以亚秒级的速度工作。

答案 3 :(得分:1)

//finds permutations of an array
#include<iostream>
#include<vector>
using namespace std;

inline void vec_in(vector<unsigned>&, unsigned&);
inline void vec_out(vector<unsigned>&);
inline void vec_sub(vector<vector<unsigned>>&, vector<unsigned>&, unsigned&);

int main(){
  unsigned size;
  cout<<"SIZE : ";
  cin>>size;
  vector<unsigned> vec;
  vec_in(vec,size);
  unsigned choose;
  cout<<"CHOOSE : ";
  cin>>choose;
  vector<vector<unsigned>> sub;
  vec_sub(sub, vec, choose);
  size=sub.size();
  for(unsigned y=0; y<size-2; y++){
    for(unsigned j=0; j<choose-1; j++){
      vector<unsigned> temp;
      for(unsigned i=0; i<=j; i++){
        temp.push_back(sub[y][i]);
      }
      for(unsigned x=y+1; x<size; x++){
        if(temp[0]==sub[x][choose-1]){break;}
        vector<unsigned> _temp;
        _temp=temp;
        for(unsigned i=j+1; i<choose; i++){
          _temp.push_back(sub[x][i]);
        }
        sub.push_back(_temp);
      }
    }
  }
  cout<<sub.size()<<endl;
  for(unsigned i=0; i<sub.size(); i++){
    vec_out(sub[i]);
  }
  return 0;
}

inline void vec_in(vector<unsigned>& vec, unsigned& size){
  for(unsigned i=0; i<size; i++){
    unsigned k;
    cin>>k;
    vec.push_back(k);
  }
}

inline void vec_out(vector<unsigned>& vec){
  for(unsigned i=0; i<vec.size(); i++){
    cout<<vec[i]<<" ";
  }
  cout<<endl;
}

inline void vec_sub(vector<vector<unsigned>>& sub, vector<unsigned>& vec, 
unsigned& size){
  for(unsigned i=0; i<vec.size(); i++){
    //if(i+size==vec.size()){break;}
    vector<unsigned> temp;
    unsigned x=i;
    for(unsigned k=0; k<size; k++){
      temp.push_back(vec[x]);
      x++;
      if(x==vec.size()){x=0;}
    }
    sub.push_back(temp);
  }
}

这不会像示例中那样以相反的顺序打印。反转阵列一次即可打印,您将完全得到答案!

背后的想法是:

1.假设您有5个数字:1 2 3 4 5,然后一次选择3个

2.依次找到子数组:
    1 2 3
    2 3 4
    3 4 5
    4 5 1
    5 1 2

3.这些将是长度为n

的数组的前n个子数组 4.现在,从第一个子数组中取出1,从第二个子数组中取出3,4,然后从这3个元素中制作另一个子数组,然后从第三个子数组中取出4,5,然后执行相同的操作。不要从最后两个子数组中获取元素,否则元素将开始重复。

5.现在从第一个子数组中取1,2,从第二个子arar中取4,以创建一个子数组,并从第3个子arar中取5,并形成一个数组。 6.将所有这些数组推回到您拥有的数组列表中。

7.从第二个子数组执行相同的模式,但不要从数组的拳头元素开始与要从要处理的数组下面的子数组中退回的最后一个元素相匹配的位置获取元素[前面的例子中,工作子arr是第一个子ar,我们并没有开始从第4个子数组中提取元素! ]

答案 4 :(得分:1)

使用std::next_permutationbitset(当前std::prev_permutation具有字典顺序,并且std::vector<bool>代替std::bitset具有动态大小):

template <typename T>
void Combination(const std::vector<T>& v, std::size_t count)
{
    assert(count <= v.size());
    std::vector<bool> bitset(count, 1);
    bitset.resize(v.size(), 0);

    do {
        for (std::size_t i = 0; i != v.size(); ++i) {
            if (bitset[i]) {
                std::cout << v[i] << " ";
            }
        }
        std::cout << std::endl;
    } while (std::prev_permutation(bitset.begin(), bitset.end()));
}

Demo