我知道在stackoverflow中已经提出了一些相关的问题。然而,这个问题更多地与3种方法之间的性能差异有关。
问题是:给定一个只包含正整数的非空数组,找出该数组是否可以分成两个子集,使得两个子集的元素之和相等。https://leetcode.com/problems/partition-equal-subset-sum/
即[1,5,11,5] =真,[1,5,9] =假
通过解决这个问题,我尝试了3种方法:
>
方法1:动态规划。从上到下的递归记忆(结果:超出时间限制):
def canPartition(nums):
total, n = sum(nums), len(nums)
if total & 1 == 1: return False
half = total >> 1
mem = [[0 for _ in range(half)] for _ in range(n)]
def dp(n, half, mem):
if half == 0: return True
if n == -1: return False
if mem[n - 1][half - 1]: return mem[n - 1][half - 1]
mem[n - 1][half - 1] = dp(n - 1, half, mem) or dp(n - 1, half - nums[n - 1], mem)
return mem[n - 1][half - 1]
return dp(n - 1, half, mem)
方法2:动态编程。自下而上。(结果:2208毫秒被接受):
def canPartition(self, nums):
total, n = sum(nums), len(nums)
if total & 1 == 1: return False
half = total >> 1
matrix = [[0 for _ in range(half + 1)] for _ in range(n)]
for i in range(n):
for j in range(1, half + 1):
if i == 0:
if j >= nums[i]: matrix[i][j] = nums[i]
else: matrix[i][j] = 0
else:
if j >= nums[i]:
matrix[i][j] = max(matrix[i - 1][j], nums[i] + matrix[i - 1][j - nums[i]])
else: matrix[i][j] = matrix[i - 1][j]
if matrix[i][j] == half: return True
return False
方法3:哈希表(字典)。结果(接受 172 毫秒):
def canPartition(self, nums):
total = sum(nums)
if total & 1 == 0:
half = total >> 1
cur = {0}
for number in nums:
cur |= { number + x for x in cur} # update the dictionary (hashtable) if key doesn't exist
if half in cur: return True
return False
对于以上3种关于时间复杂性的方法,我真的不理解两件事:
你是对的,方法 1) 和 3) 具有相同的时间复杂度,html" target="_blank">方法 2 是背包的 DP 版本(0/1),方法 1 是分支和绑定版本。您可以通过任何背包启发式方法修剪树来改进方法一,但优化必须严格,例如,如果现有总和和级别 K 的剩余元素之和为
为什么方法1)和3)的运行时间不同,
[在某种程度上]
这与python中字典的实现有更多关系。字典是由Python解释器原生实现的,对它们的任何操作都将比任何需要首先解释且更频繁的操作都要快。此外,函数调用在python中开销更高,它们是对象。所以调用一个不是简单的缓冲堆栈和jmp/调用操作。
[在很大程度上]
要考虑的另一个方面是第三种方法的时间复杂度。对于方法3,时间复杂度可以是指数的唯一方法是每次迭代导致插入与当前迭代字典中一样多的元素。
cur |= { number + x for x in cur}
上面的行应该加倍|cur|。
我认为像这样的系列是有可能的,
s={k, K2, K3,…, kn,(
(其中K是素数
我对方法 1 与其他方法之间的差异的猜测是,由于递归,方法 1 需要生成更多的堆栈帧,这比仅仅分配矩阵和迭代条件更耗费系统资源。但如果我是你,我会尝试使用某种进程和内存分析器来更好地确定和确认正在发生的事情。方法 1 根据范围分配一个矩阵,但该算法实际上将迭代次数限制为可能要少得多,因为下一个函数调用跳转到数组元素减去的总和,而不是梳理所有可能性。
方法3仅取决于输入元素的数量和可以生成的和的数量。在每次迭代中,它将输入中的当前数字添加到所有先前可实现的数字上,仅将新数字添加到该列表中。例如,给定列表[500005000050000]
,方法3最多迭代三次和:5000010000015000。但由于它取决于范围,方法2至少迭代75000*3次!
给定列表 [50000、50000、50000],方法 1、2 和 3 生成以下迭代次数:15、225000 和 6。
我尽最大努力解决leetcode中的二和问题 给定一个整数数组,返回两个数字的索引,使它们相加到一个特定的目标。 您可以假设每个输入都有一个精确的解决方案,并且您可以不使用相同的元素两次。 例子: 该计划: 1) 强力迭代len(nums)O(n) 2)使用哈希表O(1)搜索target-num[i] 使生效 我为这个解决方案努力了几个小时,但发现答案被接受了,但没有通过60分。 运行时间:60毫
我有一个我无法解决的组合问题。 给定一组向量和一个目标向量,为每个向量返回一个标量,以便集中缩放向量的平均值最接近目标。 编辑:权重w_i在范围[0,1]内。这是一个约束优化问题: 最小化d(平均(w_i*x_i),目标),条件是总和(w_i)-1=0 如果我不得不命名这个问题,它将是无界子集平均。 我已经研究过无界背包和类似的问题,但由于数字的相互依赖性,动态编程实现似乎是不可能的。 我还补充了
我分析了Java中的源代码,并得到一个关于方法的问题。 下面是JDK1.6中的方法: 我对if(e.hash==hash)的
我有一个练习: 您将获得不同面额的硬币和总金额。请编写一个函数来计算您需要的最少数量的硬币,以弥补该金额。如果硬币的任何组合都无法弥补该金额,请返回-1 例1: 我还谷歌了一个这样的解决方案: 我知道它是关于DP的,但是,我对它很困惑,比如,的含义是什么,为什么要添加,为什么要添加,为什么要添加1? 有人能用简单的英语指出出路吗?
我有一个ArrayList<code>data</code>,其中包含<code>Person</code>类型的Objects,它每n秒更新一次,并具有现有数据的总量。为了在表中显示这些数据,我使用<code>clear()</code>一个ObservableList,并使用<code>addAll(data)</ccode>来避免GUI阻塞。 我想使用HashSet作为Observable
最近我读了一个练习DP的问题。我想不出一个,所以我尝试了一个递归解决方案,后来我修改了这个解决方案,使用了记忆化。问题陈述如下:- 做出改变。你会得到n种硬币面值v(1) 我从这里得到了问题 我的解决办法如下: 这就是我如何理解我对这个问题的解决方案。假设面额以升序存储在L中。当我从结束迭代到开始时,我有一个选择,要么选择一个面额,要么不选择它。如果我选择它,我然后递归以满足剩余的金额与较低的面额