最近我对子集求和问题感兴趣,该问题是在超集中找到零和子集。我在SO上找到了一些解决方案,此外,我遇到了一个使用动态编程方法的特定solution。我根据他的定性描述在python中翻译了他的解决方案。我正在尝试对更大的列表进行优化,这会占用大量的内存。有人可以推荐优化或其他技术来解决这个特殊问题吗?这是我在python中的尝试:
import random
from time import time
from itertools import product
time0 = time()
# create a zero matrix of size a (row), b(col)
def create_zero_matrix(a,b):
return [[0]*b for x in xrange(a)]
# generate a list of size num with random integers with an upper and lower bound
def random_ints(num, lower=-1000, upper=1000):
return [random.randrange(lower,upper+1) for i in range(num)]
# split a list up into N and P where N be the sum of the negative values and P the sum of the positive values.
# 0 does not count because of additive identity
def split_sum(A):
N_list = []
P_list = []
for x in A:
if x < 0:
N_list.append(x)
elif x > 0:
P_list.append(x)
return [sum(N_list), sum(P_list)]
# since the column indexes are in the range from 0 to P - N
# we would like to retrieve them based on the index in the range N to P
# n := row, m := col
def get_element(table, n, m, N):
if n < 0:
return 0
try:
return table[n][m - N]
except:
return 0
# same definition as above
def set_element(table, n, m, N, value):
table[n][m - N] = value
# input array
#A = [1, -3, 2, 4]
A = random_ints(200)
[N, P] = split_sum(A)
# create a zero matrix of size m (row) by n (col)
#
# m := the number of elements in A
# n := P - N + 1 (by definition N <= s <= P)
#
# each element in the matrix will be a value of either 0 (false) or 1 (true)
m = len(A)
n = P - N + 1;
table = create_zero_matrix(m, n)
# set first element in index (0, A[0]) to be true
# Definition: Q(1,s) := (x1 == s). Note that index starts at 0 instead of 1.
set_element(table, 0, A[0], N, 1)
# iterate through each table element
#for i in xrange(1, m): #row
# for s in xrange(N, P + 1): #col
for i, s in product(xrange(1, m), xrange(N, P + 1)):
if get_element(table, i - 1, s, N) or A[i] == s or get_element(table, i - 1, s - A[i], N):
#set_element(table, i, s, N, 1)
table[i][s - N] = 1
# find zero-sum subset solution
s = 0
solution = []
for i in reversed(xrange(0, m)):
if get_element(table, i - 1, s, N) == 0 and get_element(table, i, s, N) == 1:
s = s - A[i]
solution.append(A[i])
print "Solution: ",solution
time1 = time()
print "Time execution: ", time1 - time0
答案 0 :(得分:5)
我不太确定你的解是精确的还是PTA(多时间近似)。
但是,正如有人指出的那样,这个问题确实是NP-Complete。
意思是,每个已知(精确)算法都对输入的大小具有指数时间行为。
意思是,如果你可以在.01纳秒内处理1个操作,那么对于59个元素的列表,它将需要:
2^59 ops --> 2^59 seconds --> 2^26 years --> 1 year
-------------- ---------------
10.000.000.000 3600 x 24 x 365
您可以找到启发式算法,它可以让您轻松找到多项式时间内的精确解。
另一方面,如果使用集合中数字值的边界来限制问题(到另一个),则问题复杂度会降低到多项式时间。但即便如此,所消耗的内存空间也将是非常高阶的多项式 消耗的内存将远远大于内存中的几千兆字节。 甚至比硬盘上的几个tera字节大得多。
(这是对集合中元素值的界限的小值)
可能是您的动态编程算法的情况。
在我看来,在构建初始化矩阵时,你使用的是1000的界限。
您可以尝试较小的界限。那就是......如果你的输入始终由小值组成。
祝你好运!
答案 1 :(得分:4)
黑客新闻的某个人想出了以下问题的解决方案,我非常喜欢。它碰巧在python:):
def subset_summing_to_zero (activities):
subsets = {0: []}
for (activity, cost) in activities.iteritems():
old_subsets = subsets
subsets = {}
for (prev_sum, subset) in old_subsets.iteritems():
subsets[prev_sum] = subset
new_sum = prev_sum + cost
new_subset = subset + [activity]
if 0 == new_sum:
new_subset.sort()
return new_subset
else:
subsets[new_sum] = new_subset
return []
我花了几分钟时间,它运作良好。
答案 2 :(得分:1)
有一篇关于优化python代码的有趣文章here。基本上主要的结果是你应该内联你的频繁循环,所以在你的情况下这意味着不是每个循环调用get_element
两次,而是将该函数的实际代码放在循环中以避免函数调用开销
希望有所帮助!干杯
答案 3 :(得分:1)
,第一眼捕捉
def split_sum(A):
N_list = 0
P_list = 0
for x in A:
if x < 0:
N_list+=x
elif x > 0:
P_list+=x
return [N_list, P_list]
一些建议:
尝试使用1D列表并使用bitarray减少内存占用(http://pypi.python.org/pypi/bitarray),这样您就可以更改get / set功能。这应该减少你的内存占用至少64(list中的整数是指向整数whit类型的指针,因此它可以是因子3 * 32)
避免使用try-catch,但在开始时找出适当的范围,你可能会发现你将获得巨大的速度。
答案 4 :(得分:0)
以下代码适用于Python 3.3+,我在Python中使用了itertools模块,它有一些很好的方法可供使用。
var p=/(\>{1}[^\n\<]*?)([^\n\<]{0,30}regu[^\n\<]{0,10})/gi,b=document.body;
b.innerHTML=b.innerHTML.replace(p,'$1<span style="background-color:red;">$2</span>');
输入输出如下:
from itertools import chain, combinations
def powerset(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
nums = input("Enter the Elements").strip().split()
inputSum = int(input("Enter the Sum You want"))
for i, combo in enumerate(powerset(nums), 1):
sum = 0
for num in combo:
sum += int(num)
if sum == inputSum:
print(combo)
答案 5 :(得分:0)
只需更改集合w中的值,并相应地使数组x与len的len一样大,然后传递subsetsum函数中的最后一个值作为你想要子集的总和,你就完成了(如果你想要的话)通过提供自己的价值来检查。
def subsetsum(cs,k,r,x,w,d):
x[k]=1
if(cs+w[k]==d):
for i in range(0,k+1):
if x[i]==1:
print (w[i],end=" ")
print()
elif cs+w[k]+w[k+1]<=d :
subsetsum(cs+w[k],k+1,r-w[k],x,w,d)
if((cs +r-w[k]>=d) and (cs+w[k]<=d)) :
x[k]=0
subsetsum(cs,k+1,r-w[k],x,w,d)
#driver for the above code
w=[2,3,4,5,0]
x=[0,0,0,0,0]
subsetsum(0,0,sum(w),x,w,7)