【问题标题】:Find the combination of numbers that is a close as can be to a specific number找到与特定数字最接近的数字组合
【发布时间】:2015-10-15 12:54:03
【问题描述】:

我有一个向量A,即

A = [300; 165; 150; 150; 400; 300; 80; 250; 165; 80; 200] 

我试图找到一组由该向量 A 的元素组成的向量,以便它们的元素总和的值尽可能接近 400,并且向量 A 的所有元素都是包含在不相交的向量集中。

例如,400 已经是 400,所以这是第一组没有松弛的向量。

另一个集合是[250 150]的向量,它们的和是400。

另外两个可以是两组向量[300 80],它们的和是380,所以20的slack就妥协了。

另一个是[165 165],它们总和为 330,余量为 70。最后一个是 200 和 150,余量为 50。总余量为 20+20+70+50=160 .

我正在尝试找到一种启发式或算法(不是编程模型),以最大限度地减少松弛。我在 Matlab 中编码。

【问题讨论】:

  • 你必须更具体一点。您是否尝试将您的集合拆分为不相交的集合,以便它们的并集包含您原始集合的所有元素?
  • 没错。我试图将它们分成不相交的集合,并且它们的联合必须包含原始集合的所有元素。我使用混合整数编程解决了这个问题,但我需要任何其他方法。
  • @aycaaltay 我仍然会理解该集合是一组满足约束的向量:A)向量由 A 的元素组成,没有重复元素。 B) 每个向量的和小于等于 400。C) Vector A 的所有元素都在其中一个向量中。
  • 松弛是方向性的,即如果总和是420,松弛是-20,总松弛将用-20计算?
  • 这是 NP-hard 通过减少分区问题。但是,您可以使用动态编程在 O(w*n) 中解决它,其中 w 是最大值,n 是数组元素的数量

标签: algorithm matlab combinatorics heuristics


【解决方案1】:

你可以试试这样的:

v = [300; 165; 150; 150; 400; 300; 80; 250; 165; 80; 200];
binarystr = dec2bin(1:(2^(length(v))-1));
bincell = mat2cell(binarystr,ones(size(binarystr ,1),1),ones(size(binarystr ,2),1));
bin = cellfun(@(x) str2double(x),bincell);

现在你可以乘以找到所有的组合:

comb = b*v;

找到最小值

target = 400;
[val,index] = min(abs(comb-target));

如果您想知道组合是什么,您可以查找索引:

idxs = find(bin(index,:));

值是:

disp(idxs)
disp(v(idxs))

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    所以我认为这是一个非常有趣的问题并在工作中开始(我希望我的老板不会发现),但我错过了一部分。代码非常可怕,但我想展示我猜想的概念。

    A = [300; 165; 150; 150; 400; 300; 80; 250; 165; 80; 200] ;
    
    

    P = (1 - (sum(A) /400 - floor(sum(A)/400))) * 400; %//minimum slack to be achieved

    %//Round 1 G1 = zeros(floor(sum(A)/400)+1,3) for t = 1:floor(sum(A)/400)+1 if size(A,1) > 1 %//single combination [F indF] = min(abs(A-400)); %//double combination if size(A >1) D = combntns(A,2); sumD = sum(D,2); [F2 indF2] = min(abs(sumD-400)); end %//triple combination if size(A >2) T = combntns(A,3); sumT = sum(T,2); [F3 indF3] = min(abs(sumT-400)); end %remove 1 [R removeInd] = min([F,F2,F3]); if removeInd == 1 G1(t,1) = A(indF); A(indF) =[]; else if removeInd ==2 G1(t,1:2) = D(indF2,:) ; [tmp,tmp2] = intersect(A,G1(t,:)); A(tmp2) = []; else removeInd == 3 G1(t,:) = T(indF3,:) ; [tmp,tmp2] = intersect(A,G1(t,:)); A(tmp2) = [] ; end end

    else if size(A,1) == 1 G1(t,1) = A; end end

    end

        </pre></code>
    

    the results:

    >>400   0   0
      150   250 0
      165   150 80
      300   80  0
      165   200 0
      300   0   0
    

    The reason the results were wrong is because I searched for subsets with length of 1,2 and 3. 4 is not possible, since it produce huge results (but you can include it anyways). If I switch to subsets with length of 1 and 2 I get the right answer. So I think the step I am missing is how long my subsets can be.

    results when max length of subset is set to 2:

    >>400 0 0 150 250 0 300 80 0 300 80 0 165 200 0 165 150 0

    你所要做的就是 % out 三元组合,然后改变这一行: [R removeInd] = min([F,F2]);没有 F3

    【讨论】:

    • 对于最大长度为 4 等的子集,您可以编写另一个循环(这将是一个嵌套循环),也可以像我一样将其写出来,但我认为这是解决方案。
    猜你喜欢
    • 2014-07-29
    • 2023-03-15
    • 2016-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-12
    相关资源
    最近更新 更多