这是来自Programming Pearls ed。 2,第2栏,问题8:
给定一组n个实数,一个实数t和一个整数k,你能多快判断该组中是否存在一个总和最多为t的k元素子集?
一个简单的解决方案是对前k个元素进行排序和求和,这是我们找到这样一个总和的最佳希望。然而,在解决方案部分,Bentley暗示了一个采用nlog(k)时间的解决方案,尽管他没有给出如何找到它的提示。我一直在努力奋斗;一个人认为我要通过列表并添加所有小于t / k的元素(在O(n)时间内);说有m1< k这样的元素,它们总和为s1<吨。然后我们需要k-m1元素,所以我们可以在O(n)时间内再次扫描列表,查找小于(t-s1)/(k-m1)的所有元素。再次加入,得到s2和m2,然后再次加上m2< k,寻找小于(t-s2)/(k-m2)的所有元素。所以:
def kSubsetSumUnderT(inList, k, t):
outList = []
s = 0
m = 0
while len(outList) < k:
toJoin = [i for i in inList where i < (t - s)/(k - m)]
if len(toJoin):
if len(toJoin) >= k - m:
toJoin.sort()
if(s + sum(toJoin[0:(k - m - 1)]) < t:
return True
return False
outList = outList + toJoin
s += sum(toJoin)
m += len(toJoin)
else:
return False
我的直觉是这可能是O(nlog(k))算法,但我很难向自己证明这一点。想法?
答案 0 :(得分:2)
考虑t> 0和all([x>t for x in inList])
的示例。 toJoin
将永远为空,您的算法甚至无法完成,更不用说O(nlog(k))。
答案 1 :(得分:0)
运行时间Theta(n log k)的自然算法可能是用k个无穷大初始化最大堆,然后遍历数组,推送新元素并弹出最大值以使堆中的k最小结束。 (正如Bentley所提到的,在Theta(n)时间选择渐近更快。在实践中选择的最佳方法可能是堆积并弹出最小k次,即Theta(n + k log n)= Theta(n)当k = O(n / log n)时。)