【问题标题】:Subset Sum : Why is DFS + pruning faster than 2 for loop?子集总和:为什么 DFS + 修剪比 2 for 循环快?
【发布时间】:2016-10-09 19:02:16
【问题描述】:

这个问题是leetcode 416 Partition Equal Subset Sum。

#2 for loop, which got TLE
class Solution(object):
    def canPartition(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        nums.sort()
        allsum = sum(nums)
        if allsum % 2 == 1:
            return False
        subsets = {() : 0}
        temp = dict(subsets)
        for each in nums:
            for subset in subsets:
                new = temp[subset] + each
                if new * 2 == allsum:
                    return True
                elif new * 2 < allsum:
                    temp[tuple(list(subset) + [each])] = new
                else:
                    del temp[subset]
            subsets = dict(temp)
        return False

DFS + pruning:
class Solution(object):
    def canPartition(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        nums.sort()
        if sum(nums) % 2 != 0:
            return False
        else:
            target = sum(nums) / 2
        return self.path(nums, len(nums), target)

def path(self, nums, length, target):#DFS + pruning
    if target == 0:
        return True
    elif target < 0 or (target > 0 and length == 0):
        return False
    if self.path(nums, length - 1, target - nums[length - 1]):
        return True
    return self.path(nums, length - 1, target)

为什么 2 for 循环比 DFS 慢?他们都有剪枝,我认为DFS的时间复杂度,这是一个np问题,应该比2 for循环差,不是吗?

【问题讨论】:

    标签: python algorithm loops recursion time-complexity


    【解决方案1】:

    仅仅因为您使用两个循环并不意味着您的算法在 O(n2) 或多项式中。算法的复杂性取决于每个循环执行多少次。 在这部分代码中:

        for each in nums:
            for subset in subsets:
                ....
    

    第一个循环将运行 n 次,因为 nums 的大小是 n 并且它不会改变。然而,subsets 的大小在每次迭代后都会变大 2 倍。所以第二个for 循环的主体将执行1 + 2 + 4 + 8 + 16 + 32 + ... + 2^n = 2^(n+1) 次。

    所以您的算法在 O(2n) 中,甚至没有考虑您在正文中执行的昂贵操作(复制 lists 和 dicts)第二个循环。

    您的第二种方法(从技术上讲不是 DFS)在案例复杂性方面等同于您的第一种方法。它们都是O(n2)。但是在第二种方法中,与第一种方法(复制lists 和dicts 等)相比,您做的额外工作更少。因此,您的第一种方法可能会运行得更快,但从长远来看并不重要。对于更大的输入,这两种方法都不够高效。

    请注意,这是一个非常著名的问题,称为Subset-sum,它是 NP-Complete。可以使用Pseudo-polynomial time中的Dynamic Programming解决。

    【讨论】:

    • 能否告诉我,如果我的第二个解决方案不是基于递归的 DFS,它是什么?我不认为这是回溯,我认为这只是遍历搜索。我对吗?非常感谢您的帮助!
    • 你可以称它为DFS,也可以称它为Backtrack,但通常我们在有显式图时使用术语DFS,在这个问题中,图是隐式的。
    • 是的!据我所知,Backtrack 只是隐式图上的 DFS!但是,我的算法没有跳回到前一个状态并沿着另一个分支继续前进的过程,这是Backtrack的核心过程(不是吗?)。那么你能告诉我,根据你的观点,Backtrack的核心概念是什么?我会阅读许多关于 Backtrack 的解释,虽然它们很模糊。非常感谢你! :D
    • 阅读this的第一段。您的代码尝试首先使用一个项目来达到目标​​,但是当它发现它无法达到目标时,它会返回并尝试在不使用该项目的情况下达到目标。我不明白你为什么看不到回溯。
    • 非常感谢!现在我对回溯有了更深入的了解!
    猜你喜欢
    • 2021-10-01
    • 1970-01-01
    • 2022-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-21
    相关资源
    最近更新 更多