【问题标题】:Subset-Sum in Linear Time线性时间的子集和
【发布时间】:2013-12-17 05:08:26
【问题描述】:

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

  1. (20 分) 令 I = {r1,r2,...,rn} 为 n 个任意正整数的集合,I 中的值为 明显。 I 没有按任何排序顺序给出。假设我们要找到 I 的子集 I',使得 I' 中所有元素的总和正好是 100*ceil(n ^.5)(I 的每个元素在I' 中最多出现一次)。提出一个 O(n) 时间的算法来解决这个问题。

据我所知,这基本上是背包问题的一个特例,也就是所谓的子集和问题......这两个问题都在 NP 中,理论上不可能在线性时间内解决?

那么……这是一个诡计的问题吗?


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

【问题讨论】:

  • 权重确实是有界的,大于100*ceil(sqrt(n))的权重可以忽略。所以你可以使用你链接到的算法。
  • 当没有解决方案时(例如I={1,2,3,4}),应该如何响应? (a) “无解” (b) “最近的解是……” (c) 在 O(n) 中不收敛。

标签: algorithm time-complexity subset-sum


【解决方案1】:

有两件事使这个问题成为可能:

  1. 输入可以被截断为大小 O(sqrt(n))。没有负输入,因此您可以丢弃任何大于 100*sqrt(n) 的数字,并且所有输入都是不同的,因此我们知道最多有 100*sqrt(n) 输入很重要。
  2. 比赛场地大小为 O(sqrt(n))。尽管有 O(2^sqrt(n)) 方法可以组合重要的 O(sqrt(n)) 输入,但您不必关心离开 100*sqrt(n) 范围或冗余命中的组合您已经可以达到的目标。

基本上,这个问题是动态编程,每个输入都以某种方式与“到达数字”空间的每个部分进行检查。

解决方案最终是确保数字不会脱离自身(通过在正确的方向上扫描),只查看每个数字一次,并为我们自己提供足够的信息以在之后重建解决方案。

这里有一些 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();
}

我还没有实际测试过上面的代码,所以要小心拼写错误。

【讨论】:

    【解决方案2】:

    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&lt;=100*ceil(n^.5)M = 100*ceil(n^.5)

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

    【讨论】:

      【解决方案3】:

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

      由于所需的总和SO(n^0.5) 的数量级,如果我们制定一个复杂度为S^2 的算法,那么我们很好,因为我们的算法应该是有效复杂度O(n)

      1. 遍历所有元素并检查值是否小于 S。如果是,则将其推送到新数组中。该数组应包含最多 S 个元素 (O(n^.5))

      2. 在 O(sqrt(n)*logn) 时间 (https://math.stackexchange.com/questions/65793/how-to-prove-log-n-leq-sqrt-n-over-natural-numbers

      现在这个问题是一个一维背包问题,其中 W = S 且元素数 = S(上限)。

      最大化物品的总重量,看是否等于S。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-19
        • 2013-06-25
        • 1970-01-01
        • 2014-03-24
        • 1970-01-01
        • 2013-03-18
        • 2020-04-16
        相关资源
        最近更新 更多