这是解决子集和的更好方法吗?

时间:2016-05-08 22:34:15

标签: python performance complexity-theory np subset-sum

我正在尝试更快地解决子集求和问题的基本方法,我想出了这个

天真的方式是oldSS函数,它测试所有2 ^ n个组合。但后来我注意到,在检查每个案例之前,计算当前场景可能的最小值和最大值,如果目标位于该范围的一侧,并且它可能是那里的解决方案,那么只有执行场景。这是newSS函数。我正在测试时间,它给了我这个

9.91289400371
0.00154754789233

我可以通过将getMinMax值缓存到全局变量中来进一步改善newSS时间。然而,对于天真的方法,使用聪明的hack将时间提高到2 ^(n / 2),将初始列表减少2然后对每个进行简单的方法,然后比较两个生成的列表。

但与2 ^(n / 2)运行时间相比,是否有人知道newSS函数的效果如何?

由于

import timeit
from random import randint

def oldSS(lst, acc, target):
    if lst:
        return oldSS(lst[1:], acc+lst[0], target) or oldSS(lst[1:], acc, target)
    return acc==target

def randomList():
    l = []
    for i in range(20):
        l.append(randint(0,1000))
    return l
def getMinMax(lst):
    mi = 0
    mx = 0
    for i in lst:
        if i < 0:
            mi += i
        elif i > 0:
            mx += i
    return (mi, mx)
def newSS(lst, acc, target):
    if lst:
        a = False
        b = False
        mimx = getMinMax(lst[1:])

        nmi = acc+lst[0] + mimx[0]
        nmx = acc+lst[0] + mimx[1]
        if target >= nmi and target <= nmx:
            a = newSS(lst[1:], acc+lst[0], target)

        nmi = acc + mimx[0]
        nmx = acc + mimx[1]
        if target >= nmi and target <= nmx:  
            b = newSS(lst[1:], acc, target)
        return a or b
    return acc==target


if __name__ == '__main__':
    print timeit.timeit('oldSS(randomList(), 0, 60)', number=10, setup="from __main__ import oldSS,randomList")
    print timeit.timeit('newSS(randomList(), 0, 60)', number=10, setup="from __main__ import newSS,getMinMax,randomList")

1 个答案:

答案 0 :(得分:2)

您的新方法被称为分支和绑定,并在算法和复杂性理论的研究中进行了非常详细的分析。

已知分支和界限会降低问题的最佳案例复杂性。然而最坏的情况永远不会改想想一下数字的集合,其中小计的下限和上限非常接近目标。在这种情况下,您无法修剪搜索空间中任何有意义的部分。平均而言,您可以显着改善运行时间。计算分支定界算法的精确平均大小写复杂度有点困难,因为分支定界方法对输入分布非常敏感。我的教育但有点生疏的猜测是你的新算法的复杂性不会低于O(2 ^ n / 2)。

如果您需要关于渐近计算复杂度的科学答案,我建议您做一些研究,或者您可以在CS stack exchange上提出问题。如果您只是想了解哪种方法更好,您可以设置经验性试验来比较它们。这样的试验将是最好的实用方法。