C ++中的分区和组合(组合)实现

时间:2013-07-04 04:13:04

标签: c++ r math combinatorics

给定大小为MN的矩阵,我们希望用整数值(> = 0)填充每一行,以便它总和到某个值。

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

这是在Partition library下的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 ++中是否存在任何实现?

2 个答案:

答案 0 :(得分:2)

递归版

这是一个递归解决方案。您有序列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("%3d\n", 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运行。

答案 1 :(得分:1)

您可以自己实施: 这样的分区由6个整数0 <= x[0] <= x[1] <= x[2] <= x[3] <= 2定义; 相应行中的值只是差异x[0]-0x[1]-x[0]x[2]-x[1]等。 如果列数(5)是固定的,则有4个嵌套循环; 它不是,你可以递归地表达问题。