线性时间中的子集和

时间:2013-12-17 05:08:27

标签: algorithm time-complexity subset-sum

这是我们的算法期末考试的问题。这是逐字的,因为教授让我们把考试的副本带回家。

  
      
  1. (20分)设I = {r1,r2,...,rn}是一组 n 任意正整数, I 中的值是不同即可。 I 未按任何排序顺序提供。假设我们想找到 I 的子集 I',使得 I'中所有元素的总和正好是100 * ceil(n ^ .5)( I 的每个元素最多只能出现在 I'中一​​次。提出一个O( n )时间算法来解决这个问题。
  2.   

据我所知,它基本上是背包问题的特殊情况,也称为子集和问题......两者都在NP中,理论上在线性时间内无法解决?

所以...这是一个棘手的问题吗?


This SO post基本上解释了如果权重是有界的,可以进行伪多项式(线性)时间近似,但在检查问题中,权重不受限制,并且考虑到检查的整体难度如果教授希望我们知道/想出一个模糊的动态优化算法,我会感到震惊。

3 个答案:

答案 0 :(得分:3)

有两件事可以解决这个问题:

  1. 可以将输入截断为大小为O(sqrt(n))。没有负输入,因此您可以丢弃任何大于100 * sqrt(n)的数字,并且所有输入都是不同的,因此我们知道最多有100 * sqrt(n)个输入。
  2. 比赛场地的大小为O(sqrt(n))。虽然有O(2 ^ sqrt(n))方法来组合重要的O(sqrt(n))输入,但您不必关心离开100 * sqrt(n)范围或冗余命中的组合你已经可以达到的目标。
  3. 基本上,这个问题尖叫动态编程,每个输入都以某种方式对“到达数字”空间的每个部分进行检查。

    解决方案的最终目的是确保数字不会自行消失(通过向正确方向扫描),只查看每个数字一次,然后给自己提供足够的信息以重建解决方案。< / p>

    以下是一些应该在给定时间内解决问题的C#代码:

    int[] FindSubsetToImpliedTarget(int[] inputs) {
        var target = 100*(int)Math.Ceiling(Math.Sqrt(inputs.Count));
    
        // build up how-X-was-reached table
        var reached = new int?[target+1];
        reached[0] = 0; // the empty set reaches 0
        foreach (var e in inputs) {
            // we go backwards to avoid reaching off of ourselves
            for (var i = target; i >= e; i--) {
                if (reached[i-e].HasValue) {
                    reached[i] = e;
                }
            }
        }
    
        // was target even reached?
        if (!reached[target].HasValue) return null;
    
        // build result by back-tracking via the logged reached values
        var result = new List<int>();
        for (var i = target; reached[i] != 0; i -= reached[i].Value) {
            result.Add(reached[i].Value);
        }
        return result.ToArray();
    }
    

    我实际上没有测试过上面的代码,所以要小心打字错误,然后再打字。

答案 1 :(得分:1)

以下是O(n)时间的简单解决方案。

由于所需的总和S大约为O(n^0.5),如果我们制定复杂度S^2的算法,那么我们就是好的,因为我们的算法应具有有效的复杂性{{ 1}}。

  1. 在所有元素上迭代一次,检查值是否小于S.如果是,则将其推入新阵列。该数组应包含最多S个元素(O(n ^ .5))

  2. 在O(sqrt(n)* logn)时间(&lt; O(n))中按降序对此数组进行排序。这是因为logn&lt; = sqrt(n)表示所有自然数。 https://math.stackexchange.com/questions/65793/how-to-prove-log-n-leq-sqrt-n-over-natural-numbers

  3. 现在这个问题是1D背包问题,W = S,元素数= S(上限)。

    最大化项目的总重量,看它是否等于S.

    可以使用线性时间的动态编程来解决(线性wrt W~S)。

答案 2 :(得分:1)

使用subset-sum problem的典型DP算法将获得O(N)耗时的算法。我们使用dp[i][k](布尔值)来指示前i个项是否具有和k的子集,转换方程为:

dp[i][k] = (dp[i-1][k-v[i] || dp[i-1][k]),

它是O(NM),其中N是集合的大小,M是目标总和。由于元素是不同的,并且总和必须等于100*ceil(n^.5),我们只需要考虑最初的100 * ceil(n ^ .5)项,然后我们得到N<=100*ceil(n^.5)M = 100*ceil(n^.5)

DP算法为O(N*M) = O(100*ceil(n^.5) * 100*ceil(n^.5)) = O(n).