子集和算法在最差时间内比2 ^(n / 2)快一点?

时间:2014-08-09 03:38:40

标签: python algorithm complexity-theory time-complexity np

在分析了在2 ^(n / 2)时间内运行的最快子集和算法之后,我注意到可以进行的轻微优化。我不确定它是否真的算作优化,如果确实如此,我想知道它是否可以通过递归来改进。

基本上来自原始算法:http://en.wikipedia.org/wiki/Subset_sum_problem(参见标题为Exponential time algorithm的部分)

  • 它接受列表并将其拆分为两个
  • 然后它在2 ^(n / 2)时间
  • 中生成两者的分类功率集
  • 然后它在两个列表中进行线性搜索,以查看两个列表中的1个值是否使用聪明的技巧加总到x

在我的优化版

  • 它获取列表并删除最后一个元素last
  • 然后它将列表分成两个
  • 然后它在2 ^((n-1)/ 2)时间
  • 中生成两者的有序功率集
  • 然后它在两个列表中进行线性搜索,以查看两个列表中的1个值是否使用聪明的技巧总和为xx-last(同时具有相同的运行时间)

如果找到了,那么我会知道它有效。我尝试使用python时间函数来测试大小为22的列表,而我的版本显然快了两倍。

运行以下代码后,显示

0.050999879837   <- the original algorithm
0.0250000953674   <- my algorithm

我对递归部分的逻辑是,如果它适用于n时间内2^((n-1)/1)列的from math import log, ceil, floor import helper # my own code from random import randint, uniform import time # gets a list of unique random floats # s = how many random numbers # l = smallest float can be # h = biggest float can be def getRandomList(s, l, h): lst = [] while len(lst) != s: r = uniform(l,h) if not r in lst: lst.append(r) return lst # This just generates the two powerset sorted lists that the 2^(n/2) algorithm makes. # This is just a lazy way of doing it, this running time is way worse, but since # this can be done in 2^(n/2) time, I just pretend its that running time lol def getSortedPowerSets(lst): n = len(lst) l1 = lst[:n/2] l2 = lst[n/2:] xs = range(2**(n/2)) ys1 = helper.getNums(l1, xs) ys2 = helper.getNums(l2, xs) return ys1, ys2 # this just checks using the regular 2^(n/2) algorithm to see if two values # sum to the specified value def checkListRegular(lst, x): lst1, lst2 = getSortedPowerSets(lst) left = 0 right = len(lst2)-1 while left < len(lst1) and right >= 0: sum = lst1[left] + lst2[right] if sum < x: left += 1 elif sum > x: right -= 1 else: return True return False # this is my improved version of the above version def checkListSmaller(lst, x): last = lst.pop() x1, x2 = x, x - last return checkhelper(lst, x1, x2) # this is the same as the function 'checkListRegular', but it checks 2 values # at the same time def checkhelper(lst, x1, x2): lst1, lst2 = getSortedPowerSets(lst) left = [0,0] right = [len(lst2)-1, len(lst2)-1] while 1: check = 0 if left[0] < len(lst1) and right[0] >= 0: check += 1 sum = lst1[left[0]] + lst2[right[0]] if sum < x1: left[0] += 1 elif sum > x1: right[0] -= 1 else: return True if left[1] < len(lst1) and right[1] >= 0: check += 1 sum = lst1[left[1]] + lst2[right[1]] if sum < x2: left[1] += 1 elif sum > x2: right[1] -= 1 else: return True if check == 0: return False n = 22 lst = getRandomList(n, 1, 3000) startTime = time.time() print checkListRegular(lst, -50) # -50 so it does worst case scenario startTime2 = time.time() print checkListSmaller(lst, -50) # -50 so it does worst case scenario startTime3 = time.time() print (startTime2 - startTime) print (startTime3 - startTime2) 列表,我们能不能一次又一次地重复这个吗?

这有什么意义,还是我完全错了?

由于

我创建了这个python代码:

def dec_to_bin(x):
    return int(bin(x)[2:])

def getNums(lst, xs):
    sums = []
    n = len(lst)
    for i in xs:
        bin = str(dec_to_bin(i))
        bin = (n-len(bin))*"0" + bin
        chosen_items = getList(bin, lst)
        sums.append(sum(chosen_items))
    sums.sort()
    return sums

def getList(binary, lst):
    s = []
    for i in range(len(binary)):
        if binary[i]=="1":
            s.append(float(lst[i]))
    return s

这是我刚刚用来生成powerset列表的帮助程序库。

{{1}}

1 个答案:

答案 0 :(得分:3)

  

然后它在2 ^((n-1)/ 2)时间

中生成两者的有序幂集

好的,因为现在这个名单少了一个。然而,这并不是一个大问题,它只是2^(1/2) ...

的恒定时间改进
  

然后它在两个列表中进行线性搜索,以查看两个列表中的1个值是否使用巧妙的技巧总和为x或x-last(同时具有相同的运行时间)

...而且这种改进将会消失,因为现在你做了两倍的操作来检查x和x-last总和而不仅仅是x

  

我们不能一次又一次地重复这个吗?

不,你不能,因为你不能一次又一次地分割原始算法。这个技巧只适用于一次,因为一旦你开始在两个以上的列表中查找值,你就不能再使用排序技巧了。