分区问题暴力算法

时间:2015-12-31 21:28:42

标签: algorithm set complexity-theory computer-science theory

我正在尝试在bruteforce中为下面的分区问题做伪代码。

  

一组整数X和整数k(k> 1)。找到X的k个子集   每个子集中的数字总和为相同数量而不是两个   子集具有共同的元素,或者推断出没有这样的k个子集   存在。问题是NP-Complete

     

例如,X = {2,5,4,9,1,7,6,8}和k = 3,可能   解决方案将是:{2,5,7},{4,9,1},{6,8}因为所有这些   总计14。

对于穷举搜索我知道通常我们必须搜索每个可能的解决方案,看看目标是否相似。但由于这是分区问题,这可能很棘手。

算法暴力:

Subset= X.sum/K //I had a guess this would make the parition
For int i==1; I <subset; i++ // this would change partition if not found in the first one
If (j=0; I<n; i++)
    Sum == s[i]
    If sum == target 
        Display “found”
    Else 
    “not found”

2 个答案:

答案 0 :(得分:4)

以下是JavaScript中的一个示例,它可以提供正数组元素。算法弹出堆栈,如果有效则通过检查已完成部件的数量输出结果;否则,它依次获取每个数组元素并将另一组参数添加到堆栈中,其中一个数组元素是空白部分的第一个添加,另一个是它们依次添加到每个部分填充。 (为方便起见,result累积为一个字符串,其中部分索引位于每个数组元素之前。)

var arr = [2,5,4,9,1,7,6,8]
var k = 3;

var n = arr.length;
var target = arr.reduce( (prev, curr) => prev + curr ) / k;
var sums = [];
for (var i=0; i<k; i++){
  sums[i] = 0;
}

var stack = [[0,sums,0,""]];

while (stack[0] !== undefined){
  var params = stack.pop();

  var i = params[0];
  var sums = params[1];
  var done = params[2];
  var result = params[3];

  if (done == k){
    console.log(result);
    continue;
  } else if (i == n){
    continue;
  }

  var was_first_element = false;

  for (var j=0; j<k; j++){
    if (!was_first_element && sums[j] == 0){
      was_first_element = true;
      var _sums = sums.slice();
      _sums[j] += arr[i];
      stack.push([i + 1,_sums,done + (_sums[j] == target ? 1 : 0),result + j + ": " + arr[i] +", "]);
    } else if (sums[j] != 0 && arr[i] + sums[j] < target && i < n - 1){
      var _sums = sums.slice();
      _sums[j] += arr[i];
      stack.push([i + 1,_sums,done,result + j + ": " + arr[i] +", "]);
    } else if (sums[j] != 0 && arr[i] + sums[j] == target){
      var _sums = sums.slice();
      _sums[j] += arr[i];
      stack.push([i + 1,_sums,done + 1,result + j + ": " + arr[i] +", "]);
    }
  }
}

输出:

/*
0: 2, 1: 5, 0: 4, 1: 9, 2: 1, 2: 7, 2: 6, 0: 8
{2,4,8} {5,9} {1,7,6}

0: 2, 1: 5, 0: 4, 1: 9, 0: 1, 0: 7, 2: 6, 2: 8
{2,4,1,7} {5,9} {6,8}

0: 2, 0: 5, 1: 4, 1: 9, 1: 1, 0: 7, 2: 6, 2: 8
{2,5,7} {4,9,1} {6,8}
*/

答案 1 :(得分:0)

我将从@ m69提供的评论开始。由于您知道必须使用所有元素,因此您知道分区的总和将等于整个集合的总和。添加了每个分区具有相同总和的知识,然后您可以确定k个分区,每个分区都需要具有totalSum / k的总和。这提供了一种快速方法来检测无法分割为k子集的许多集合。此代码可能如下所示:

if (totalSum % k != 0)
{
    /* The set can't be partitioned into k elements */
}

现在是时候开始构建一些分区了。我建议使用递归解决方案。因此,我们将从一个函数开始,该函数接受一个数组以及该数组的每个分区应该具有的总和。

check_partition(array, partitionSum)
{
    /* code goes here */
}

我们的递归会有两个基本情况。首先,如果给定的数组的总和等于分区总和,那么我们的分区就成功了。在这种情况下,我们将返回数组。

arraySum = sum(array);
if (sum(array) == partitionSum)
{
    return array;
}

另一种基本情况是,如果数组的总和小于分区的目标总和,则此尝试失败。在这种情况下,我们返回null以指示给定的分区不起作用。

if (sum(array) < partitionSum)
{
    return null;
}

现在编写递归步骤。对于此步骤,我们希望从数组中选择一个元素,并构建与包含此数字的目标相加的每个分区。数组中最大的元素是执行此操作的最佳元素,因为它将具有最少的可能分区。然后对于该集合中的每个分区,我们希望从较大的数组中删除其中的元素,然后使用该较小的数组再次运行该函数。

maxElementPartitions = buildAllMaxElementPartitions(array, partitionSum);
foreach(partition in maxElementPartitions)
{
    arrayWithoutPartition = removePartition(array, partition)
    resultSet = check_partition(arrayWithoutPartition, partitionSum);
    if (resultSet == null)
    {
        /* It failed down the chain of recursion somewhere */
        /* Move to the next iteration of the loop */
    }
    else
    {
        return = resultSet + partition;
    }
}
/* If the loop exits no results were found */
return null;

这个递归函数将返回一个成功的分区,如果不存在,它将返回null。