在分析了在2 ^(n / 2)时间内运行的最快子集和算法之后,我注意到可以进行的轻微优化。我不确定它是否真的算作优化,如果确实如此,我想知道它是否可以通过递归来改进。
基本上来自原始算法:http://en.wikipedia.org/wiki/Subset_sum_problem(参见标题为Exponential time algorithm
的部分)
x
在我的优化版
中last
x
或x-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}}
答案 0 :(得分:3)
然后它在2 ^((n-1)/ 2)时间
中生成两者的有序幂集
好的,因为现在这个名单少了一个。然而,这并不是一个大问题,它只是2^(1/2)
...
然后它在两个列表中进行线性搜索,以查看两个列表中的1个值是否使用巧妙的技巧总和为x或x-last(同时具有相同的运行时间)
...而且这种改进将会消失,因为现在你做了两倍的操作来检查x和x-last总和而不仅仅是x
我们不能一次又一次地重复这个吗?
不,你不能,因为你不能一次又一次地分割原始算法。这个技巧只适用于一次,因为一旦你开始在两个以上的列表中查找值,你就不能再使用排序技巧了。