r-在C++中的划分与组合(组合数学)实现

r - Partition and Composition (combinatorics) implementation in C++

本文关键字:组合 实现 C++ 划分      更新时间:2023-10-16

给定一个大小为MN的矩阵,我们希望用整数值(>=0)填充每一行,使其相加为某个值。

请注意,MN的维度是使用特定公式预先计算的,因此保证在给定所需条件(即下面的sum_val)的情况下匹配填充。

这是在分区库下的R中实现的。

library(partitions)
# In this example, we impose condition 
# that each rows must sum up to 2 in total
# And each row has 5 columns
sum_val <- 2
n <- 5
#The above two parameters are predefined.
t(as.matrix(compositions(sum_val, n)))
      [,1] [,2] [,3] [,4] [,5]
 [1,]    2    0    0    0    0
 [2,]    1    1    0    0    0
 [3,]    0    2    0    0    0
 [4,]    1    0    1    0    0
 [5,]    0    1    1    0    0
 [6,]    0    0    2    0    0
 [7,]    1    0    0    1    0
 [8,]    0    1    0    1    0
 [9,]    0    0    1    1    0
[10,]    0    0    0    2    0
[11,]    1    0    0    0    1
[12,]    0    1    0    0    1
[13,]    0    0    1    0    1
[14,]    0    0    0    1    1
[15,]    0    0    0    0    2

C++中是否有任何现有的实现?

递归版本

这是一个递归解决方案。你有一个序列a,在那里你可以跟踪你已经设置的数字。每次递归调用都会为循环中的其中一个元素分配有效的数字,然后为列表的其余部分递归调用该函数。

void recurse(std::vector<int>& a, int pos, int remaining) {
  if (remaining == 0) { print(a); return; }
  if (pos == a.size()) { return; }
  for (int i = remaining; i >= 0; --i) {
    a[pos] = i;
    recurse(a, pos + 1, remaining - i);
  }
}
void print_partitions(int sum_val, int n) {
  std::vector<int> a(n);
  recurse(a, 0, sum_val);
}

概念验证在http://ideone.com/oJNvmu.

迭代版本

您在下面的评论表明存在性能问题。虽然I/O很可能占用了您的大部分性能,但这里有一个迭代解决方案,可以避免递归方法的函数调用开销。

void print_partitions(int sum_val, int n) {
  int pos = 0, last = n - 1;
  int a[n]; // dynamic stack-allocated arrays are a gcc extension
  for (int i = 1; i != n; ++i)
    a[i] = 0;
  a[0] = sum_val;
  while (true) {
    for (int i = 0; i != last; ++i)
        printf("%3d ", a[i]);
    printf("%3dn", a[last]);
    if (pos != last) {
      --a[pos];
      ++pos;
      a[pos] = 1;
    }
    else {
      if (a[last] == sum_val)
        return;
      for (--pos; a[pos] == 0; --pos);
      --a[pos];
      int tmp = 1 + a[last];
      ++pos;
      a[last] = 0;
      a[pos] = tmp;
    }
  }
}

打印内容的总体思路和顺序与递归方法相同。不是维护计数器remaining,而是将所有令牌(或您正在分区的任何令牌)立即放在它们所属的位置,以便打印下一个分区。pos总是最后一个非零字段。如果这不是最后一个,那么您可以通过从pos中获取一个令牌并将其移动到之后的位置来获得下一个分区。如果是最后一个,则从最后一个位置获取所有令牌,找到在此之前的最后一个非零位置,并从中获取一个令牌,然后将所有这些令牌转储到获取单个令牌的位置之后的位置。

演示运行于http://ideone.com/N3lSbQ.

您可以自己实现:这样的分区由6个整数CCD_ 9定义;对应行中的值只是差x[0]-0x[1]-x[0]x[2]-x[1]等。如果列数(5)是固定的,则有4个嵌套循环;如果不是,你可以递归地公式化这个问题。