我如何在C 中制作算法,以在不重复的情况下查找集合的变化(即n元素,选择k)

How can I make an algorithm in C++ for finding variations of a set without repetition (i.e. n elements, choose k)?

本文关键字:情况下 查找 变化 集合 选择 元素 算法      更新时间:2023-10-16

例如,(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的工作速度非常慢(这是我需要的)。

这是我的代码:

#include <iostream>
#include <algorithm>
#define pb push_back
using namespace std;
int main() {
    vector <int> s = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int k = 4; // (n = 10, k = 4)
    map <string, int> m; // To check if we already have that variation
    vector <string> v; // Variations
    do {
        string str = "";
        for (int i = 0; i < k; i++) str += to_string(s[i]);
        if (m[str] == 0) {
            m[str] = 1;
            v.pb(str);
        }
    } while (next_permutation(s.begin(), s.end()));
    return 0;
}

如何制作更快的算法?

此代码生成了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);
}

123124125132134135142143145152153154213214215231234235241243245251253254312314315321324325341342345351352354412413415421423425431432435451452453512513514521523524531532534541542543

您可以在每个子集上迭代bitmask。

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

当您不需要便携式代码时,您可以使用

__builtin_popcount(int)

至少在GCC中使用X86处理器在二进制表示中获得1s的数量。

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);
    }
}

slowness是由于生成所有n!即使仅需要一小部分,排列也是如此。您的复杂性在O(n! * k log n)附近,其中 o(k log n)是对所有排列查询std::map的复杂性的上限。

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的次秒速度工作

//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个子阵列中获得1,从第二个子阵列中获得3,4,然后从这3个元素中进行另一个子阵列,然后从第三个子阵列中获得4,5,然后做同样的事情。不要从最后两个子阵列中获取元素,因为此后元素将开始重复。

5.现在从第一个子阵列中取1,2,从第二个子钻中拿出4个子阵列,并从第三个子钻中取5个,然后制作一个数组




6.将所有这些阵列推回您拥有的数组列表。

7.从第二个子阵列中执行相同的模式以前的情况下,工作子ARR是第一个,我们没有开始从第四个子数组中获取元素!]

使用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

相关文章: