所有可能的组合,使得所有数字的总和是固定数字

时间:2015-04-16 05:56:54

标签: matlab

我需要找到数字1:8的所有可能组合,使得所有元素的总和等于8 组合需要按升序排列。

例如

1 7

2 2 4

1 3 5

1 2 2 3

1 1 1 1 1 1 1 1

一个数字可以重复。但组合不能.. 即1 2 2 3和2 1 2 3 我需要按升序排列解决方案所以每种组合只有一种可能性

我在Find vector elements that sum up to specific number in MATLAB

上尝试了一些在线建议的代码
   VEC = [1:8];
      NUM = 8;
      n = length(VEC);
      finans = zeros(2^n-1,NUM);
      for i = 1:(2^n - 1)
        ndx = dec2bin(i,n) == '1';
        if sum(VEC(ndx)) == NUM
        l = length(VEC(ndx));
        VEC(ndx)
        end
      end

但他们不包括数字重复的可能性。

3 个答案:

答案 0 :(得分:2)

编辑:看看我的second more efficient answer

天真的方法!可以找到cartprod.m函数here

% Create all permutations
p(1:8) = {0:8};
M = fliplr( cartprod( p{:} ) );

% Check sums
r = sum( M, 2 ) == 8;
M = M(sum( M, 2 ) == 8,:);   % Solution here

肯定有更高效的解决方案,但如果您只需要一个快速而肮脏的小排列解决方案,这将有效。请注意,这使得Matlab需要3.5 GB的RAM来临时存储排列。


答案 1 :(得分:2)

我通过递归找到了更好的方法,而且它更优雅优雅(我更喜欢优雅)并且比我以前的尝试更快( 0.00399705213秒在我的计算机上)

编辑:您需要我的自定义函数stretchmat.m,它会拉伸矢量以适合另一个矩阵的大小。有点像repmat但伸展第一个参数(详见帮助)。非常有用!

script.m

% Define funciton to prepend a cell x with a variable i
cellprepend = @(x,i) {[i x]};

% Execute and time function
tic;
a = allcomb(cellprepend,1,8);   % Solution in a
toc;

allcomb.m

function a = allcomb( cellprepend, m, n )
    % Add entire block as a combination
    a{1} = n;

    % Exit recursion if block size 1
    if n == 1
        return;
    end

    % Recurse cutting blocks at different segments
    for i = m:n/2
        b = allcomb(cellprepend,i,n-i);
        a = [a cellfun( cellprepend, b, num2cell( stretchmat( i, b ) ) )];
    end
end

所以这个想法很简单,因为添加到8的解决方案是详尽无遗的。如果您只查找有效答案,则可以通过将问题分解为2个块来进行深度优先搜索。这可以像我上面那样以递归方式编写,有点类似于Merge Sort。 allcomb调用采用块大小(n)并找到将其分解为更小块的所有方法。

我们想要非零件,所以我们从1:n-1循环它。然后它将第一个块预先添加到第二个块的所有组合。通过仅对其中一个块进行所有梳理,我们可以确保所有解决方案都是唯一的。

至于排序,我不太清楚升序是什么意思。根据我的看法,您似乎按升序从最后一个数字中排序。你确定吗?任何种类都可以附加到script.m。

的末尾

编辑2/3 备注

  • 对于具有置换性的唯一案例,可以找到代码here
  • 感谢@Simon帮助我多次QA代码

答案 2 :(得分:0)

首先在单元格数组中保存重复的所有组合。为此,请使用nmultichoosek

v = 1 : 8;
combs = cell(length(v),0);
for i = v
    combs{i} = nmultichoosek(v,i);
end

通过这种方式,combs的每个元素都包含一个矩阵,其中每一行都是一个组合。例如,i-th combs{4}行是四个数字的组合。

现在你需要检查总和。要对所有组合执行此操作,请使用cellfun

sums = cellfun(@(x)sum(x,2),combs,'UniformOutput',false);

sums包含具有所有组合之和的向量。对于 例如,sums{4}具有组合combs{4}的数字之和。

下一步是检查固定金额。

fixed_sum = 10;
indices = cellfun(@(x)x==fixed_sum,sums,'UniformOutput',false);

indices包含逻辑值数组,告知组合是否满足固定总和。例如,indices{4}(1)会告诉您第一个包含4个数字的组合是否总和为fixed_sum

最后,检索新单元格数组中的所有有效组合,同时对它们进行排序。

valid_combs = cell(length(v),0);
for i = v
    idx = indices{i};
    c = combs{i};
    valid_combs{i} = sortrows(c(idx,:));
end

valid_combs是一个类似于combs的单元格,但只有总和达到所需数值的组合,并按使用的数字排序:valid_combs{1}包含所有有效组合1个数字,valid_combs{2},包含2个数字,依此类推。此外,由于sortrows,还会对具有相同数量的组合进行排序。例如,如果fixed_sum = 10valid_combs{8}

 1     1     1     1     1     1     1     3
 1     1     1     1     1     1     2     2

此代码非常高效,在我的旧笔记本电脑上,我可以在0.016947秒内运行它。