【问题标题】:Efficiently computing vector combinations高效计算向量组合
【发布时间】:2011-01-04 02:04:45
【问题描述】:

出于好奇,我正在研究一个研究问题,但我不知道如何编写我想到的逻辑。让我给你解释一下:

例如,我有四个向量,

v1 = 1 1 1 1
v2 = 2 2 2 2
v3 = 3 3 3 3
v4 = 4 4 4 4

现在我要做的是按组合方式添加它们,即,

v12 = v1+v2
v13 = v1+v3
v14 = v1+v4
v23 = v2+v3
v24 = v2+v4
v34 = v3+v4

到这一步就好了。现在的问题是我想将这些向量中的每一个添加一个来自 v1、v2、v3、v4 的向量,它以前没有添加过。例如:

v3 和 v4 还没有添加到 v12 中,所以我想创建 v123 和 v124。类似地,对于所有的向量,

v12 should become:
v123 = v12+v3
v124 = v12+v4

v13 should become:
v132 // This should not occur because I already have v123
v134

v14 should become:
v142 // Cannot occur because I've v124 already
v143 // Cannot occur

v23 should become:
v231 // Cannot occur
v234 ... and so on.

重要的是我不要一开始就一步到位。例如,我可以做(4 选择 3)4C3 并完成它,但我想在每次迭代中一步一步地做。

我该如何编程?

P.S.:我正在尝试研究数据挖掘中先验算法的修改版本。

【问题讨论】:

  • 一个更具体的标题会很好。

标签: c++ algorithm vector combinations


【解决方案1】:

在 C++ 中,给定以下例程:

template <typename Iterator>
inline bool next_combination(const Iterator first,
                                   Iterator k,
                             const Iterator last)
{
   /* Credits: Thomas Draper */
   if ((first == last) || (first == k) || (last == k))
      return false;
   Iterator itr1 = first;
   Iterator itr2 = last;
   ++itr1;
   if (last == itr1)
      return false;
   itr1 = last;
   --itr1;
   itr1 = k;
   --itr2;
   while (first != itr1)
   {
      if (*--itr1 < *itr2)
      {
         Iterator j = k;
         while (!(*itr1 < *j)) ++j;
         std::iter_swap(itr1,j);
         ++itr1;
         ++j;
         itr2 = k;
         std::rotate(itr1,j,last);
         while (last != j)
         {
            ++j;
            ++itr2;
         }
         std::rotate(k,itr2,last);
         return true;
      }
   }
   std::rotate(first,k,last);
   return false;
}

然后您可以继续执行以下操作:

int main()
{
   unsigned int vec_idx[] = {0,1,2,3,4};

   const std::size_t vec_idx_size = sizeof(vec_idx) / sizeof(unsigned int);

   {
      // All unique combinations of two vectors, for example, 5C2
      std::size_t k = 2;
      do
      {
         std::cout << "Vector Indicies: ";
         for (std::size_t i = 0; i < k; ++i)
         {
           std::cout << vec_idx[i] << " ";
         }
      }
      while (next_combination(vec_idx,
                              vec_idx + k,
                              vec_idx + vec_idx_size));
   }

   std::sort(vec_idx,vec_idx + vec_idx_size);

   {
      // All unique combinations of three vectors, for example, 5C3
      std::size_t k = 3;
      do
      {
         std::cout << "Vector Indicies: ";
         for (std::size_t i = 0; i < k; ++i)
         {
           std::cout << vec_idx[i] << " ";
         }
      }
      while (next_combination(vec_idx,
                              vec_idx + k,
                              vec_idx + vec_idx_size));
   }

   return 0;
}

**注1:* 因为next_combination例程的面向迭代器的接口,任何支持通过迭代器进行前向迭代的STL容器也可以使用,例如std::vectorstd::dequestd::list只是仅举几例。

注2:这个问题非常适合记忆技术的应用。在这个问题中,您可以创建一个地图并用给定组合的向量和填充它。在计算给定向量集的总和之前,您可以查看是否已经计算了总和的任何子集并使用这些结果。尽管您执行的求和非常便宜且快速,但如果您执行的计算要复杂得多且耗时得多,那么这种技术肯定会有助于带来一些重大的性能改进。

【讨论】:

  • 非常感谢。我正在寻找的是您的注释 2:“创建一个地图并用给定组合的向量和填充它。在计算给定向量集的总和之前,您可以查找总和的任何子集已经计算并使用这些结果”。如果你能举个例子,这将非常有帮助。提前致谢。
【解决方案2】:

我认为这个问题可以通过标记出现的组合来解决。

我的第一个想法是,您可以使用 3 维数组来标记发生了什么组合。但这不是很好。

用于标记的位数组(例如整数)怎么样?如:

Num 1 = 2^0 for vector 1
Num 2 = 2^1 for vector 2
Num 4 = 2^2 for vector 3
Num 8 = 2^3 for vector 4

作曲时,只需添加所有代表号码。例如,向量 124 的值是:1 + 2 + 8 = 11。这个值对于每个组合都是唯一的。

这只是我的想法。希望对你有所帮助。

编辑:也许我对我的想法不够清楚。我会尽量解释清楚一点:

1) 为每个向量分配一个代表数字。这个数字是向量的 id,它是唯一的。此外,这些数的每个子集的总和是唯一的,这意味着如果我们有 k 个代表数的总和是 M;我们可以很容易地知道哪些向量参与了求和。

我们通过赋值来做到这一点:向量 1 为 2^0; 2^1 用于向量 2;向量 3 为 2^2,依此类推...

每个 M = sum (2^x + 2^y + 2^z + ... ) = (2^x OR 2^y OR 2^z OR ...)。我们知道向量 (x + 1), (y + 1), (z +1) ... 参与求和。这可以很容易地通过以二进制方式表示数字来检查。

例如,我们知道:

2^0 = 1(二进制) 2^1 = 10(二进制) 2^2 = 100(二进制) ...

因此,如果总和是 10010(二进制),我们知道向量(数字:10)和向量(数字:10000)加入总和。

而且这里的和最好用“OR”运算符计算,如果用二进制表示也很容易理解。

2)利用上述事实,每次在计算向量的总和之前,您都可以先加/或它们的代表数。您可以通过查找数组之类的方式跟踪它们。如果总和已经存在于查找数组中,则可以省略它。这样就可以解决问题了。

【讨论】:

  • 我对位数组不熟悉。你的回答似乎很有帮助,但你能解释一下使用它吗?
  • 而且这似乎只适用于较小的矢量大小。如果我有 100 个向量怎么办?
  • @Sunil:我已经更新了答案,请检查一下。对不起,我的英语不太清楚。
【解决方案3】:

也许我误解了,但这不等于生成 1、2、3、4 的所有子集(幂集),然后为幂集的每个元素,对向量求和吗?例如:

//This is pseudo C++ since I'm too lazy to type everything
//push back the vectors or pointers to vectors, etc.
vector< vector< int > > v = v1..v4;

//Populate a vector with 1 to 4
vector< int > n = 1..4

//Function that generates the power set {nil, 1, (1,2), (1,3), (1,4), (1,2,3), etc.
vector< vector < int > > power_vec = generate_power_set(n);

//One might want to make a string key by doing a Perl-style join of the subset together by a comma or something...
map< vector < int >,vector< int > > results;
//For each subset, we sum the original vectors together
for subset_iter over power_vec{
    vector<int> result;
    //Assumes all the vecors same length, can be modified carefully if not.
    result.reserve(length(v1));
    for ii=0 to length(v1){
        for iter over subset from subset_iter{
            result[ii]+=v[iter][ii];
        }
    }
    results[*subset_iter] = result;
}

如果这是您的想法,您仍然需要一个幂集函数,但如果您搜索幂集,则很容易找到该代码。例如, Obtaining a powerset of a set in Java.

【讨论】:

  • 感谢您的努力,但据我了解,您正试图一次完成所有组合。一步一步做很重要,因为我希望每次迭代的输出都可以使用。即在每次迭代“k”时,我只想要 (k+1) 元素子集。例如,在第一次迭代中,我想要所有只有两个元素的子集,比如 v12。你明白我在说什么吗?
【解决方案4】:
  1. 维护一个用于选择两个值的所有列表。
  2. 创建一个集合向量,使集合由原始向量中的元素和 4C2 元素组成。迭代原始向量,并为每个向量添加/创建包含步骤 1 中元素的集合。维护集合向量,仅当集合不存在时,将结果添加到向量中。
  3. 总结您在步骤 2 中获得的集合向量。

但正如你所说,最简单的是 4C3。

这是用 Python 编写的。您可以将其应用于 C++

import itertools

l1 = ['v1','v2','v3','v4']
res = []
for e in itertools.combinations(l1,2):
    res.append(e)

fin = []
for e in res:
    for l in l1:
        aset = set((e[0],e[1],l))
        if aset not in fin and len(aset) == 3:
            fin.append(aset)
print fin

这将导致:

[set(['v1', 'v2', 'v3']), set(['v1', 'v2', 'v4']), set(['v1', 'v3', 'v4']), set(['v2', 'v3', 'v4'])]

这与 4C3 的结果相同。

【讨论】:

  • 我无法理解您到底想说什么。你能详细说明一下吗?谢谢
  • 我刚做了。抱歉,我最初误解了您最初的问题。
猜你喜欢
  • 1970-01-01
  • 2013-10-17
  • 2013-03-11
  • 1970-01-01
  • 2012-12-05
  • 2018-06-26
  • 2020-03-21
  • 2021-12-17
  • 1970-01-01
相关资源
最近更新 更多