检查是否存在具有n的和的倍数的数组k的子集

时间:2015-01-07 23:27:58

标签: java arrays algorithm

晚上好,我有一个带有n整数的java数组。我想检查是否有满足条件的条目 k 的子集:

m 条目的总和是 m 的倍数。

我怎样才能尽可能有效地做到这一点?我需要检查n!/k!(n-k)!个子集。

3 个答案:

答案 0 :(得分:2)

您可以使用动态编程。状态是(前缀长度,总和模m,子集中的元素数)。转换是显而易见的:我们要么添加一个数字(增加子集中的元素数量并计算新的模数m),要么我们只增加前缀长度(不添加当前数字)。如果您只需要一个是/否答案,则只能存储最后一层值并应用位优化来更快地计算转换。时间复杂度是O(n * m * k),或者具有比特优化的大约n * m * k / 64运算。空间复杂度为O(m * k)。对于成千上万的元素来说看起来是可行的。通过位优化,我的意思是使用C ++中的bitset之类的东西,它可以使用按位运算同时对一组位执行操作。

答案 1 :(得分:0)

我不喜欢这个解决方案,但它可能适合您的需求

public boolean containsSubset( int[] a , int currentIndex, int currentSum, int depth, int divsor, int maxDepth){

    //you could make a, maxDepth, and divisor static as well


    //If maxDepthis equal to depth, then our subset has k elements, in addition the sum of
    //elements must be divisible by out divsor, m
    //If this condition is satisafied, then there exists a subset of size k whose sum is divisible by m
    if(depth==maxDepth&&currentSum%divsor==0) 
         return true;

    //If the depth is greater than or equal maxDepth, our subset has more than k elements, thus
    //adding more elements can not satisfy the necessary conditions
    //additionally we know that if it contains k elements and is divisible by m, it would've satisafied the above condition. 
    if(depth>=maxdepth)
         return false;


    //boolean to be returned, initialized to false because we have not found any sets yet
    boolean ret = false;

    //iterate through all remaining elements of our array 
    for (int i = currentIndex+1; i < a.length; i++){
    //this may be an optimization or this line
    //for (int i = currentIndex+1; i < a.length-maxDepth+depth; i++){



         //by recursing, we add a[i] to our set we then use an or operation on all our subsets that could 
         //be constructed from the numbers we have so far so that if any of them satisfy our condition (return true) 
         //then the value of the variable ret will be true
         ret |= containsSubset(a,i,currentSum+a[i],depth+1,divisor, maxDepth);

    } //end for


    //return the variable storing whether any sets of numbers that could be constructed from the numbers so far.
    return ret;

}

然后调用此方法

 //this invokes our method with "no numbers added to our subset so far" so it will try adding 
 // all combinations of other elements to determine if the condition is satisfied.
 boolean answer = containsSubset(myArray,-1,0,0,m,k);

编辑:

你可以通过取所有模数(%)m和删除重复来优化它。对于n和/或k值较大但m值较小的示例,这可能是一个非常大的优化。

编辑2:

我列出的上述优化没有帮助。您可能需要重复才能获得正确的信息。我的坏。

快乐的编码!如果您有任何问题,请告诉我们!

答案 2 :(得分:0)

如果数字有下限和上限,则可能更好:

  1. 迭代所有multiple n lower_bound * k < multiple < upper_bound * k
  2. 使用动态编程检查数组中是否存在总和multiple的子集(请参阅Subset Sum problem)。
  3. 复杂性为O(k^2 * (lower_bound + upper_bound)^2)。这种方法可以进一步优化,我相信仔细思考。

    否则,您可以找到大小为k的所有子集。复杂性为O(n!)。使用回溯(伪代码):

    function find_subsets(array, k, index, current_subset):
      if current_subset.size = k:
        add current_subset to your solutions list
        return
    
      if index = array.size:
        return
    
      number := array[index]
      add number to current_subset
      find_subsets(array, k, index + 1, current_subset)
      remove number from current_subset
      find_subsets(array, k, index + 1, current_subset)