簡體   English   中英

如何在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)?

例如, (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);
}

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

您可以使用位掩碼迭代每個子集。

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

緩慢是由於生成所有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

這些將是長度為n的數組的前n個子數組

4.現在,從第一個子陣列取1,從第二個子陣列取3,4,從這3個元素中取出另一個子陣列,然后從第3個子陣列取4,5並做同樣的事情。 不要從最后兩個子數組中獲取元素,因為元素將開始重復。

5.現在從第一個子陣列取1,2,從第二個子陣列取4個制作一個子陣列,從第3個子陣列取5個並制作一個陣列

6.將所有這些陣列推回到您擁有的陣列列表中。

7.從第二個子數組執行相同的模式,但不要從數組的第一個元素開始匹配的元素開始匹配您將從正在處理的數組下面的子數組中返回的最后一個元素[In在前一種情況下,工作子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()));
}

演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM