【问题标题】:combinations totaling to sum组合总和
【发布时间】:2014-02-25 08:32:37
【问题描述】:

如何生成一个矩阵,其中所有可能的数字组合总计为重复的总和?

基本上,x1x2x3 的组合使得x1 + x2 + x3 = n

例如:n =3

0 1 2 
0 2 1
1 0 2
1 2 0
1 1 1

有没有使用预定义的 Matlab 函数的简单方法?

我试过了

n=6;
nchoosek(0:n,3)

这给了我

 0     1     2
 0     1     3
 0     1     4
 0     1     5
 0     1     6
 0     2     3
 0     2     4
 0     2     5
 0     2     6
 0     3     4
 0     3     5
 0     3     6
 0     4     5
 0     4     6
 0     5     6
 1     2     3
 1     2     4
 1     2     5
 1     2     6
 1     3     4
 1     3     5
 1     3     6
 1     4     5
 1     4     6
 1     5     6
 2     3     4
 2     3     5
 2     3     6
 2     4     5
 2     4     6
 2     5     6
 3     4     5
 3     4     6
 3     5     6
 4     5     6

如何提取总数等于n 的所有行? 我认为线性索引或find 应该可以实现,但我不知道该怎么做。

问候

【问题讨论】:

  • 您将使用逻辑索引:result = c(sum(c,2)==n,:);,其中cnchoosek 的结果。但是nchoosek 不会这样做,因为它不会重复
  • @LuisMendo 真的,谢谢!
  • 执行此操作的标准方法是想象将两个分隔线放入一行 6 个对象中,以获得 3 个单独的分区。因此,您正在考虑置换 8 个事物:6 个对象和 2 个分隔符的组合。您应该能够操纵例如nchoosek(1:8, 2) 的结果来满足您的需求。这比生成所有组合并选择具有正确总和的组合要高效得多。
  • 例如,这里是 Python 中对应的东西:[(a, b-a-1, 7-b) for a, b in itertools.combinations(range(8), 2)]。 (我这里没有 Matlab 可供试验,但你或其他人也许可以翻译这个。)

标签: matlab combinations permutation


【解决方案1】:

具体而言,让我们以 3 个值加起来为 6 的示例为例。执行此操作的标准方法是将 2 个“分隔符”放入一行 6 个相同的“对象”中:然后这些分隔符将对象分开分成3组,你可以读出每组的长度。所以我们需要做的就是列举所有放置这些分隔符的方式。您可以为此使用nchoosek(1:8, 2):该矩阵的每一行都描述了一个划分,通过描述2 + 6 == 8 对象+ 分隔器中2 个分隔器的位置。

这是一种比枚举整数 0-6 的所有三元组然后挑选出加到正确总数中的那些更有效的方法。

我不会说 MATLAB,所以以下内容可能是单调的(欢迎提出改进建议!),但这样的东西应该可以工作:

% Total we're aiming for.                                                             
n = 6;                                                                                
% Number of pieces to divide that total into.                                         
k = 3;                                                                                
% All possible placements of internal dividers.                                       
dividers = nchoosek(1:(n+k-1), k-1);                                                  
ndividers = size(dividers, 1);                                                        
% Add dividers at the beginning and end.                                              
b = cat(2, zeros(ndividers, 1), dividers, (n+k)*ones(ndividers, 1));                  
% Find distances between dividers.                                                    
c = diff(b, 1, 2) - 1

这是this site提供的结果:

c =

   0   0   6
   0   1   5
   0   2   4
   0   3   3
   0   4   2
   0   5   1
   0   6   0
   1   0   5
   1   1   4
   1   2   3
   1   3   2
   1   4   1
   1   5   0
   2   0   4
   2   1   3
   2   2   2
   2   3   1
   2   4   0
   3   0   3
   3   1   2
   3   2   1
   3   3   0
   4   0   2
   4   1   1
   4   2   0
   5   0   1
   5   1   0
   6   0   0

【讨论】:

    【解决方案2】:

    使用dec2base 生成所有重复的组合,使用logical indexing 仅保留具有所需总和的组合:

    n = 6;
    m = 3;
    c = dec2base(0:(n+1)^m-1,n+1,m)-'0'; %// generate combinations with repetition
    result = c(sum(c,2)==n,:); %// keep those with desired sum. Logical indexing
    

    【讨论】:

    • 也感谢您的解释!但是,6 0 0 0 6 00 0 6 不应该也包括在内吗?
    • @Ash 对不起。已更正。请立即尝试
    【解决方案3】:

    我相信您正在描述restricted integer partitions 的排列,尽管您的示例似乎并不完整。对于 n=3 从元素 {0, 1, 2} 分成三部分,有两种解决方案:{0, 1, 2} 和​​ {1, 1, 1}。这些可以进一步排列成:

    {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}, {1, 1, 1}}
    

    如果这不是您想要的,您应该说明为什么不包括这些额外的订单。

    如果这种理解是正确的,您应该能够通过搜索该短语找到许多资源。一些例子:

    Elegant Python code for Integer Partitioning

    Integer Partition in Java

    Algorithm for generating integer partitions

    Integer Partition Algorithm by Jerome Kelleher

    Integer Partition Algorithm by Daniel Scocco

    Fast Algorithms for Generating Integer Partitions (PDF)(看起来很耐用)

    Stony Brook Algorithm Repository - Partitions

    作为一个实际的例子,使用 Mathematica 可以这样写:

    IntegerPartitions[6, {3}, Range[0, 5]]
    

    输出:

    {{5, 1, 0}, {4, 2, 0}, {4, 1, 1}, {3, 3, 0}, {3, 2, 1}, {2, 2, 2}}
    

    然后可以对这些每个进行排列以产生其他排序。

    很容易设置一个递归函数来生成这些分区,方法是从所有数字 n 开始,然后附加 n 的值。这一直持续到每个列表的长度为 p;如果列表总数为n,则保留;如果没有,则将其丢弃。同样,在 Mathematica 中:

    f[n_, p_, c__] /; Length@{c} == p := If[+c == n, {{c}}, {}]
    f[n_, p_, c___] := Array[f[n, p, c, #] &, Min[c, n - +c] + 1, 0, Join]
    
    f[6, 3]
    

    输出:

    {{2, 2, 2}, {3, 2, 1}, {3, 3, 0}, {4, 1, 1}, {4, 2, 0}, {5, 1, 0}, {6, 0, 0}}
    

    【讨论】:

      【解决方案4】:

      函数 nchoosek 提供了在总和中选择 r-1 个加号的可能方法。例如,x1 + x2 + x3 = 5,则必须在总和 1 + 1 + 1 + 1 + 1 = 5 上选择两个信号。对于非负解,进行变换 yi = x1 + 1,并求解 y1+y2+ 。 .. = m + n

      nchoosek 的结果提供了加号的位置。

      程序的其余部分提供所选信号之间的总和。

      clear all    
      close all    
      clc    
      
      % Program that generates the possible distributions
      % M objects in r boxes 
      % Éderson D'Martin Costa    
      % 12/02/2015    
      
      % number of objects
      m = 3;    
      
      % number of boxes    
      r = 3;
      
      % total number of possibilities
      % C = nchoosek(m+r-1,r-1)
      
      v = 1:m+r-1;    
      C = nchoosek(v,r-1);    
      [l,c] = size(C);    
      Y = zeros(l,c+1);    
      Y(:,1) = C(:,1);    
      Y(:,end) = m+r-C(:,end);
      
      for i = r-1:-1:2    
          Y(:,i) = C(:,i)-C(:,i-1);    
      end    
      
      X = Y-1;    
      display(X)    
      % sum(X,2)
      

      【讨论】:

      • 请将其翻译成英文和/或添加正确的答案解释。
      【解决方案5】:

      正如@Mark Dickinson 指出的那样。我只是想给出直观的解释。

      您的问题可以重述为“您可以将 3 个苹果分配给 3 个人有多少种方式”?

      假设你有 3 个人:AA、BB、CC,你想在他们之间分配 3 个苹果。你是怎么做到的?

      AA   |    BB     | CC
      ***  |           | 
      *    |     *     | *
      **   |     *     | 
      ........
      * -> represent apples.
      

      现在,如果您认为问题只是在 5 个位置中选择两个 |,我们知道可以使用 5 选择 2。

      【讨论】:

        猜你喜欢
        • 2022-08-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-05
        • 1970-01-01
        相关资源
        最近更新 更多