我有一个python问题需要解决,但是我得到了一些关于解决方案性能不佳的评论......所以我想在这里分享它,并检查可能的改进:
问题很简单,就是要找到列表第一部分之和与第二部分之和的最小绝对差值。
作为一个例子,如果我们有:
L = [3, 1, 2, 4, 3]
我们可以将其分为四个阶段:
i = 1, abs difference = |3 − 10| = 7
i = 2, abs difference = |4 − 9| = 5
i = 3, abs difference = |6 − 7| = 1
i = 4, abs difference = |10 − 3| = 7
在此示例中,脚本应返回1
作为最小绝对差值。
正如我所说,对我来说这很容易,我做了:
def get_minAbsDiff(L):
return min([abs(sum(L[0:i+1]) - sum(L[i+1:])) for i in xrange(len(L)-1)])
然而,似乎这是时间复杂的解决方案O(N * N)
是否可以为此问题获得O(N)?
修改
有人告诉我这个O(N * N)的复杂性,我实际上并不知道这个例子是否属实。
答案 0 :(得分:5)
实现O(N)解决方案的关键是要认识到,当您在列表中移动时,您将减去一个总和并添加到另一个总和。所以......
def get_minAbsDiff(L):
leftSum = 0
rightSum = sum(L)
minDiff = rightSum
for i in L:
leftSum += i
rightSum -= i
diff = abs(leftSum-rightSum)
if diff < minDiff: minDiff = diff
return minDiff
答案 1 :(得分:2)
您可以通过一次从右和到左移一个值来递增计算总和。
以下是我的编写方式,sums
左右生成xs
的所有分区,并产生其总和。
def sums(xs):
left, right = 0, sum(xs)
for x in xs:
yield left, right
left += x
right -= x
yield left, right
def min_abs_diff(xs):
return min(abs(left - right) for left, right in sums(xs))
print min_abs_diff([3, 1, 2, 4, 3])
答案 2 :(得分:2)
您无需对所有元素进行求和。创建总和一次并在循环中更新它:
def min_abs_diff(L):
sum1, sum2 = 0, sum(L)
min_abs_diff = float('inf') # sentinel value
for i in L:
sum1 += i
sum2 -= i
abs_diff = abs(sum1 - sum2)
if min_abs_diff > abs_diff:
min_abs_diff = abs_diff
return min_abs_diff
所以你从0
和13
开始,然后在循环中,当你将3
的值从一个移到10
时,它变为i
和sum()
总结到另一个。
您的解决方案是O(N * N),因为{{1}}函数也循环。因此,对于列表推导中的每次迭代,当您将所有N个元素合计为两个总计时,您需要N个步骤,并且您有N个这样的迭代。
答案 3 :(得分:0)
L = [3, 1, 2, 4, 3]
sec_part = sum(L)
diff = []
for i in L:
diff.append(abs(sec_part - i * 2))
sec_part -= i * 2
print min(diff)
首先,总结整个列表,复杂度为O(N)
第二,使用for循环来减少值,ps:我们应该减少项目的两倍,因为我们在第一步添加它。 for循环时间复杂度也是O(N)
第三,使用min
找出最小值,时间复杂度也是O(N)
答案 4 :(得分:0)
我知道这里有太多解决方案。只是想在时间和空间上最容易添加算法。
testList = [1,2,3,4,5]
totalSum = sum(testList)
currentSum = 0
minDiff = totalSum
for a in testList:
currentSum += a
minDiff = min( abs(totalSum - currentSum - currentSum), minDiff)
print minDiff