在Matlab中用约束找到变量的“可能”组合的算法?

时间:2012-11-20 22:03:27

标签: algorithm matlab combinations probability

假设我在A中有7个项目,在B中有4个项目

A=[10;40;90;130;200;260;320]
B=[100;300;500;1000]

我希望列出可能的组合,其中:

  • 必须包含 A 的所有子组件
  • B的子组件可以添加直到所添加的所有子组件的 SUM 大于2000

任何人都知道如何在Matlab中做到这一点?

我的尝试:

X=sum(A);
y=1;
for Y=1:((length(A))-1);
   X=X+B(y);
   if(X>2000)
       disp('Following is unacceptable')    
   end
   y=y+1
end

但是这段代码不正确。它只是添加B的第一个元素,然后添加第二个元素,依此类推。它没有为我提供可能的组合。

示例:

  • sum(A)+ B(1)= OK
  • sum(A)+ B(4)= NOT OK
  • sum(A)+ B(1)+ B(2)= OK
  • sum(A)+ B(2)+ B(3)= OK
  • 等...

如果将来A或B的值发生变化,我希望自动化。我不确定这是否也是概率的情况。

3 个答案:

答案 0 :(得分:4)

只需使用nchoosek和双for - 循环即可浏览B中所有可能的元素组合:

SA = sum(A);
for k = 1:numel(B)
    for idx = nchoosek(1:numel(B), k)'
        B_subset = B(idx);
        if (SA + sum(B_subset) <= 2000)
            disp([A(:)', B_subset(:)'])
        end
    end
end

这将打印所有小于(或等于)2000的组合。对于您的示例,我们得到:

    10    40    90   130   200   260   320   100
    10    40    90   130   200   260   320   300
    10    40    90   130   200   260   320   500
    10    40    90   130   200   260   320   100   300
    10    40    90   130   200   260   320   100   500
    10    40    90   130   200   260   320   300   500
    10    40    90   130   200   260   320   100   300   500

说明:

内部for - 循环
内部for - 循环使用nchoosek(1:numel(B), k),它生成1 ...长度(B)中的所有k长度组合(我正在使用numel而不是length出于习惯;在这种情况下,它具有相同的效果)。例如,在我们的案例中,B有4个元素,因此对于k = 3,我们得到nchoosek(1:4, 3)

    1   2   3
    1   2   4
    1   3   4
    2   3   4

我们得到的是B中元素索引的所有可能的k长度组合。在每次迭代中,此for - 循环为idx指定不同的索引组合。我们如何将B的索引转换为真实元素?我们只写B(idx) 在内部循环中测试组合:如果总sum(A) + sum(B(idx))小于(或等于)2000,则显示该组合。

外部for - 循环
外部for - 循环简单地遍历所有可能的组合长度(即,k的所有可能值)。

希望有所帮助!

P.S:

未来的一些MATLAB编程技巧:
1.变量名称区分大小写 2.您不需要增加循环变量。 for循环会自动为您执行此操作。

答案 1 :(得分:2)

假设B不是很长(大约10个元素),通过所有组合进行详尽搜索都可以正常工作。您可以使用递归函数执行此详尽搜索,但下面的代码使用了一种在MATLAB中特别简洁的技巧:它通过将每个组合表示为二进制位字符串来扫描B的所有元素组合。

% examine each of 2^length(B) combinations
for i=0:2^length(B)-1
    % converts the binary string into an array of 0 and 1 used to select elements in B
    combo = dec2bin(i, length(B))-'0'; 
    % print the combination of elements if their sum is large
    if combo * B + sum(A) > 2000
       disp(find(combo));
    end
end

有2 ^长度(B)可能的组合。这将依次检查它们,将组合表示为长度长度(B)的二进制字符串,并评估这些元素的总和(带有位串和B之间的点积)。

答案 2 :(得分:2)

最好的方法是涉及一些递归,如下所示:

sumA=sum(A);
find_CombinationsOfB(B,sumA,[])

function ret=findCombinationsOfB(in_vals,total_sum,already_contained)

if total_sum>2000
    ret=false;
else
    for y=1:length(in_vals);
       if (~findCombinationsOfB(in_vals([1:(y-1);(y+1):length(in_vals)],total_sum+in_vals(y),[already_contained in_vals(y))
          display([already_contained in_vals])
       end
    end
    ret=true;
end

基本上它的作用是尝试B的每个组合。它将打印任何不加起来的2000,包括来自A的总和。

一步一步,这是它的作用:

  1. 最初,传递完整的B数组以及A的总和。传递一个空数组来存储到目前为止使用了B的哪些元素。
  2. 每个元素依次添加到函数中,并使用新的和再次调用,并且数组中缺少值。
  3. 如果阵列总和超过2000,它就会停止推理链。
  4. 如果您想了解更多有关其工作方式的信息,请在函数开头打印in_vals,total_sum和already_contained,如下所示:

    fprintf("in_vals=%s   total_sum=%i   already_contained=%s",mat2str(in_vals),total_sum,mat2str(already_contained));
    

    它应该在每次迭代时向您显示正在发生的事情。