在Matlab中找到具有约束的变量的“可能”组合的算法?
Posted
技术标签:
【中文标题】在Matlab中找到具有约束的变量的“可能”组合的算法?【英文标题】:Algorithm to find the 'possible' combinations of variables with constraints in Matlab? 【发布时间】:2012-11-09 02:20:36 【问题描述】:假设我在 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) = 不行 sum(A) + B(1) + B(2) = OK sum(A) + B(2) + B(3) = OK 等等……如果将来 A 或 B 的值发生变化,我希望这可以自动化。我不确定这是否也是概率问题。
【问题讨论】:
那么最佳解决方案的标准是什么?金额最高的那个?总和超过 2000 的任何解决方案是否可以接受,或者它是 B 中元素数量最少/最多的解决方案?请说明您的问题。 已经声明任何高于 2000 的解决方案都是不可接受的。我只想要总和小于 2000 的所有可能组合。 【参考方案1】:只需使用nchoosek
和双for
-loop 即可遍历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
-loop:
内部的for
-loop 使用nchoosek(1:numel(B), k)
,它生成所有 k 长度的组合 1...length(B)(出于习惯,我使用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
-loop:
外部for
-循环简单地遍历所有可能的组合长度(即,遍历k
的所有可能值)。
希望有帮助!
附注:
一些面向未来的 MATLAB 编程技巧:
1.变量名区分大小写。
2. 你不需要增加循环变量。 for
循环会自动为您执行此操作。
【讨论】:
哇,这是所有答案中最简单的。您能否对代码发表评论并描述其工作原理?很想学习。 @NLed 我假设这是因为A
和B
不是所有行或所有列。我已经修复了我的答案,看看它现在是否有效。
谢谢,这是所有答案中最简单的!
@NLed 您可以将它们存储在元胞数组中,因为它们的大小是可变的。
@NLed 普通数组不适合这里,原因在我上一篇文章中所述。有时 MATLAB 会以这种方式显示元胞数组的内容。您可以使用:
语法转储内容。或者,您可以编写一个 for 循环并单独打印每个单元格。【参考方案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 的总和。
一步一步,它的作用如下:
-
最初,传递 B 的完整数组以及 A 的总和。传递一个空数组以存储迄今为止已使用 B 的哪些元素。
每个元素依次添加到函数中,并使用新的总和再次调用,并且数组中缺少一个值。
如果数组总和在任何时候超过 2000,就会停止推理链。
如果您想了解更多关于它的工作原理,请在函数开头打印 in_vals、total_sum 和 already_contained,如下所示:
fprintf("in_vals=%s total_sum=%i already_contained=%s",mat2str(in_vals),total_sum,mat2str(already_contained));
它应该在每次迭代中向您展示正在发生的事情。
【讨论】:
您能否详细说明代码的作用?我想了解它是如何工作的,而不是复制它。 我收到以下错误:??? Undefined function or method 'find_CombinationsOfB' for input arguments of type 'double'.
大概是怎么设置的。你必须要么把函数放在它自己的文件中,要么把基础文件变成一个函数。【参考方案3】:
假设 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^length(B) 种可能的组合。这将依次检查它们,将组合表示为长度为 length(B) 的二进制字符串,并评估这些元素的总和(位字符串和 B 之间的点积)。
【讨论】:
如果您能对代码的确切作用添加更多解释将不胜感激,我真的很想学习,而不仅仅是复制/粘贴代码 所以这显示了可以与 A 一起添加的 B 的值对吗??打印的值是可以与 sum(A) 结合的值? 打印的值是 B 的条目的索引,可以与 A 的条目组合,使它们的总和超过 2000。如果你想打印 B 的元素(而不是它们的索引) ,写B(find(combo))
而不是find(combo)
。
并且我假设如果我将不等式反转为
以上是关于在Matlab中找到具有约束的变量的“可能”组合的算法?的主要内容,如果未能解决你的问题,请参考以下文章