【问题标题】:Generate all possible choices of elements each from a distinct set从不同的集合中生成所有可能的元素选择
【发布时间】:2017-12-11 08:57:29
【问题描述】:

假设我们有k 集合,每个集合都包含q 元素。我想生成所有可能的集合,我们从每个集合中恰好选择 1 个元素。假设集合被表示为一个表,其中每一行是一个集合,它的列是它的元素。还假设所有元素都像这样按行索引

设置 1:1 2 3

第二组:4 5 6

第 3 组:7 8 9

问题是 k,q 可能会有所不同,所以我不能使用嵌套的 for 循环。我在 C++ 中工作,这个结构实际上是 std::vectorintstd::vector,但我不是在这里要求代码,只是关于如何做到这一点的想法。

【问题讨论】:

  • “问题是 k,q 可能会发生变化,所以我不能使用嵌套的 for 循环。” 为什么?
  • @CinCout 我可能会遗漏一些东西,但我不知道有什么方法可以根据需要生成尽可能多的嵌套循环。就我而言,我需要 k 个嵌套循环。请解释一下。
  • kq 各不相同,但已知对吗?在您给出的示例中,您总共将拥有 27 套。我只是让循环运行q^k 次。有什么问题?
  • @CinCout 我的意思是每次 k 更改时您都必须更改代码,以添加或删除不切实际的嵌套循环。请提供一些代码或伪代码来阐明您的想法。
  • 查看DAle的答案

标签: c++ algorithm set cartesian-product


【解决方案1】:

递归

using sets = vector<vector<int>>;

void rec(int index, const sets& s, vector<int>& v) {
    for (int next : s[index]) {
        v[index] = next;
        if (index + 1 == s.size()) {
            output(v);
        } else {
            rec(index+1, s, v);
        }
    }
}

int main() {
    sets s = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int q = s[0].size();
    vector<int> v(q);
    rec(0, s, v);
    return 0;
}

非递归

主要思想是每个选择都可以用 base-q 数字系统中的数字编码。您需要做的就是用length &lt;= n 遍历所有base-q 数字。数字的每一位都是对应集合中的一个索引。

例如,我们有 3 个数字的 2 个集合。您需要遍历{00, 01, 02, 10, 11, 12, 20, 21, 22}

using sets = vector<vector<int>>;

void non_rec(const sets& s) {
    int q = s[0].size();
    int k = s.size();
    vector<int> v(q);
    int cnt = (int)pow(q, k);

    for (int i = 0; i < cnt; ++i) {
        int tmp = i;
        for (int j = 0; j < k; ++j) {
            v[j] = s[j][tmp % q];
            tmp /= q;
        }
        output(v);
    }
}

int main() {
    sets s = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    non_rec(s);
    return 0;
}

http://ideone.com/V58I7W

【讨论】:

  • @GauravSehgal,在主循环的每次迭代中,我们只需在 q-ary 数字系统中获取 i 的数字,并将它们用作相应集合中的索引。
  • 这假设 pow(q,k) 适合 int,这在某些设置和/或平台中可能太小。最好对 i 和 cnt 使用 unsigned long。此外,在某些嵌入式平台上,pow() 返回的“double”类型可能只有单精度。用整数乘法在 for 循环中计算 cnt 比较省钱。
  • @JoachimWagner,我同意你的观点,但这只是一个示例代码,不是生产就绪的。它缺乏对极端情况的关注(例如,它不适用于空集)、可能的溢出、变量的好名称。但另一方面,它并没有填充过多会使代码复杂化的细节
【解决方案2】:

硬编码解决方案是

for (int a1 : v[0]) {
  for (int a2 : v[1]) {
    for (int a3 : v[2]) {
      for (int a4 : v[3]) {
        for (int a5 : v[4]) {
            do_job(a1, a2, a3, a4, a5);
        }
      }
    }
  }
}

为了使其通用,您可以这样做:

bool increase(const std::vector<std::set<int>>& v,
              std::vector<std::set<int>::iterator>& it)
{
    for (std::size_t i = 0, size = it.size(); i != size; ++i) {
        const std::size_t index = size - 1 - i;
        ++it[index];
        if (it[index] == v[index].end()) {
            it[index] = v[index].begin();
        } else {
            return true;
        }
    }
    return false;
}

template <typename F>
void iterate(const std::vector<std::set<int>>& v, F&& do_job)
{
    std::vector<std::set<int>::iterator> its;
    its.reserve(v.size());
    for (auto& s : v) {
        its.push_back(s.begin());
    }

    do {
        do_job(its);
    } while (increase(v, its));
}

Demo

【讨论】:

    【解决方案3】:

    这样可以吗?

    std::vector<std::vector<int>> v;
    for(auto& r : v)
        for(auto i : r)
            // use i
    

    【讨论】:

      【解决方案4】:

      如果您不知道集合的数量和每个集合中的元素数量,那么您可以通过以下方式生成您想要的所有元素的集合。基本上,您可以遍历集合的所有元素,并将其包含在迄今为止计算的任何内容的先前剩余乘积中。如果还有什么不清楚的地方请告诉我

      #include <iostream>
      #include <set>
      #include <tuple>
      #include <vector>
      
      using std::cout;
      using std::endl;
      
      void append_results(const std::set<int>& set,
                          std::vector<std::vector<int>>& results);
      
      int main() {
      
          auto sets = std::vector<std::set<int>>{
              std::set<int>{1, 2, 3},
              std::set<int>{4, 5, 6},
              std::set<int>{7, 8, 9}
          };
      
          auto results = std::vector<std::vector<int>>{};
      
          for (const auto& set : sets) {
              append_results(set, results);
          }
      
          for (const auto& result : results) {
              for (auto integer : result) {
                  cout << integer << " ";
              }
              cout << endl;
          }
      
          return 0;
      }
      
      void append_results(const std::set<int>& set,
                          std::vector<std::vector<int>>& results) {
      
          if (results.empty()) {
              for (auto integer : set) {
                  results.push_back(std::vector<int>{integer});
              }
          } else {
              auto old_results = results;
              results.clear();
              for (auto integer : set) {
                  for (auto old_result : old_results) {
                      old_result.push_back(integer);
                      results.push_back(std::move(old_result));
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        您可以在这里使用回溯来生成所有子集

          void func(vector<vector<int> > arr,int row,vector<int> &temp,vector<vector<int> > &ans)
          {
            if(row>=arr.size())
            {
                ans.push_back(temp);    //You have generated one of the answers.store it
                return;
            }
            for(int i=0;i<arr[row].size();i++)
            {
                temp.push_back(arr[row][i]);  //Pick an element from current set
                func(arr,row+1,temp,ans);     //recurse for next set
                temp.pop_back();              //Remove from current set to include next element from this set(for loop)
            }
          }
        

        工作代码:http://ideone.com/RvHMNT

        【讨论】:

          猜你喜欢
          • 2013-08-11
          • 1970-01-01
          • 2018-01-06
          • 1970-01-01
          • 2016-10-12
          • 2023-03-29
          • 1970-01-01
          相关资源
          最近更新 更多