如何将数组拆分为两个子集,并使数组的子值总和尽可能相等

时间:2012-10-28 18:58:14

标签: javascript jquery arrays algorithm

我真的需要一个算法大师!所以问题就是我得到了一个像这样的数组:

[
    [870, 23]
    [970, 78]
    [110, 50]
]

我希望将其拆分,以便它看起来像这样:

// first array
[
    [970, 78]
]
// second array
[
    [870, 23]
    [110, 50]
]

所以现在,为什么我也希望它看起来像这样?

因为我希望保持子值的总和尽可能相等。因此970约为870 + 11078约为23 + 50。 因此,在这种情况下它很容易,因为如果你只是拆分它们而只看第一个子值它已经是正确的但我想检查两者并保持它们尽可能相等,这样它也可以使用一个有100个子阵列的数组!所以,如果有人能告诉我我可以编程的算法,那真的很棒!

音阶:

  • 〜数组中的1000个元素(子列表)
  • 元素是最多10 ^ 9
  • 的整数

我正在寻找“足够接近的解决方案” - 它不一定是最佳解决方案。

4 个答案:

答案 0 :(得分:2)

首先,正如已经确定的那样 - 问题是NP-Hard,减少了分区问题。

Reduction
给定partition problem的实例,创建每个大小为1的列表。结果将是这个问题。

以上结论
这个问题是NP-Hard,没有已知的多项式解。

第二,由于问题的严重性,任何指数和伪多项式解决方案都需要很长时间才能工作。

第三,它给我们留下了启发式和近似算法。

我建议采用以下方法:

  1. 规范化子列表的比例,因此所有元素都将处于相同的比例(例如,所有元素将被标准化为范围[-1,1]或者所有元素将标准化为标准normal distribution)。
  2. 创建一个新列表,其中每个元素将是规范化列表中匹配子列表的总和。
  3. 使用为子集和/分区问题开发的一些近似或启发式解决方案。
  4. 结果不是最佳的,但最佳效果在这里确实无法解决。

答案 1 :(得分:2)

根据我在原帖中的讨论中收集的内容,您不是在搜索单个分裂点,而是想要在两个集合中分配所有对,以便两个集合中的每个集合中的总和大致相等。

由于足够接近的解决方案是可以接受的,也许您可​​以尝试基于模拟退火的方法? (见http://en.wikipedia.org/wiki/Simulated_annealing

简而言之,我们的想法是,您可以通过随机将每对配​​置为左侧或右侧设置来开始。 接下来,您可以通过

生成新状态
  • a)从左到右移动随机选择的一对,
  • b)移动随机选择的一对 从右到左设置,或
  • c)同时做两件事。

接下来,确定此新状态是好于还是差于当前状态。如果它更好,请使用它。 如果情况更糟,只有当接受概率函数接受它时才接受它,这是一个函数 最初允许使用更糟糕的状态,但随着时间的推移越来越少地支持它们(或者温度降低",在SA术语中)。 经过大量的迭代(比如100.000),你应该有一个非常好的结果。

(可选)多次重新运行此算法,因为它可能会陷入局部最优(尽管接受概率函数试图对此进行反击)。

这种方法的优点是实施起来很简单,你可以自己决定多长时间 你希望它继续寻找更好的解决方案。

答案 2 :(得分:1)

我假设我们只是在阵列中间寻找一个位置,将其分成第一和第二部分。

似乎线性算法可以做到这一点。在JavaScript中有这样的东西。

arrayLength = 2;
tolerance = 10;

// Initialize the two sums.
firstSum = [];
secondSum = [];
for (j = 0; j < arrayLength; j++)
{
   firstSum[j] = 0;
   secondSum[j] = 0;
   for (i = 0; i < arrays.length; i++)
   {
      secondSum += arrays[i][j];
   }
}

// Try splitting at every place in "arrays".
// Try to get the sums as close as possible.
for (i = 0; i < arrays.length; i++)
{
   goodEnough = true;
   for (j = 0; j < arrayLength; j++)
   {
      if (Math.abs(firstSum[j] - secondSum[j]) > tolerance)
         goodEnough = false;
   }

   if (goodEnough)
   {
      alert("split before index " + i);
      break;
   }

   // Update the sums for the new position.
   for (j = 0; j < arrayLength; j++)
   {
      firstSum[j] += arrays[i][j];
      secondSum[j] -= arrays[i][j];
   }
}

答案 3 :(得分:0)

感谢所有答案,暴力攻击是一个好主意,NP-Hard也与此相关,但事实证明这是一个多背包问题,可以使用this pdf document解决。