例子:
list = [1,2,3,10]
sum = 12
result = [2,10]
注:我了解从大小为 n 的列表中查找相加结果等于另一个数字的算法(但我不会读取 C# 代码,无法确定它是否适合我的需求。我在 Linux 上尝试使用 Mono,但出现错误,而且无法弄清楚如何使用 C# :(
并且我也了解对于所有组合的列表数字求和的算法(但它似乎非常低效。我不需要所有的组合。)
list = [1,2,3,10]
sum = 12
result = [2,10]
注:我了解从大小为 n 的列表中查找相加结果等于另一个数字的算法(但我不会读取 C# 代码,无法确定它是否适合我的需求。我在 Linux 上尝试使用 Mono,但出现错误,而且无法弄清楚如何使用 C# :(
并且我也了解对于所有组合的列表数字求和的算法(但它似乎非常低效。我不需要所有的组合。)
这个问题可以转化为0-1背包问题,其中您需要找到一个精确和的集合。解决方案取决于约束条件,在一般情况下,这个问题是NP完全的。
然而,如果最大搜索和(我们称之为S
)不太高,则可以使用动态规划来解决该问题。我将使用递归函数和记忆化来解释它,这比自下而上的方法更容易理解。
让我们编写一个函数f(v,i,S)
,它返回v [i:]
中恰好等于S
的子集数。要递归解决它,首先我们必须分析基础(即:v [i:]
为空):
S == 0:只有[]
的子集的总和为0,因此它是有效的子集。由于这个原因,函数应该返回1。
S!= 0:由于只有[]
的子集的总和为0,因此没有有效的子集。因此,函数应该返回0。
然后,让我们分析递归情况(即:v[i:]
不为空)。有两种选择:将数字 v[i]
包含在当前子集中或不包含。如果我们包含 v[i]
,那么我们正在寻找具有和为 S - v[i]
的子集,否则,我们仍在寻找和为 S
的子集。函数 f
可以按照以下方式实现:
def f(v, i, S):
if i >= len(v): return 1 if S == 0 else 0
count = f(v, i + 1, S)
count += f(v, i + 1, S - v[i])
return count
v = [1, 2, 3, 10]
sum = 12
print(f(v, 0, sum))
通过检查f(v, 0, S) > 0
,您可以知道是否有解决方案。然而,这段代码太慢了,每个递归调用会生成两个新的调用,导致O(2^n)算法。现在,我们可以应用记忆化使其在时间复杂度为O(n*S)的情况下运行,如果S
不太大,则更快:
def f(v, i, S, memo):
if i >= len(v): return 1 if S == 0 else 0
if (i, S) not in memo: # <-- Check if value has not been calculated.
count = f(v, i + 1, S, memo)
count += f(v, i + 1, S - v[i], memo)
memo[(i, S)] = count # <-- Memoize calculated result.
return memo[(i, S)] # <-- Return memoized value.
v = [1, 2, 3, 10]
sum = 12
memo = dict()
print(f(v, 0, sum, memo))
g
的函数,它返回一个和为S
的子集。为了实现这一点,只需在至少存在包含它们的解决方案时添加元素即可。def f(v, i, S, memo):
# ... same as before ...
def g(v, S, memo):
subset = []
for i, x in enumerate(v):
# Check if there is still a solution if we include v[i]
if f(v, i + 1, S - x, memo) > 0:
subset.append(x)
S -= x
return subset
v = [1, 2, 3, 10]
sum = 12
memo = dict()
if f(v, 0, sum, memo) == 0: print("There are no valid subsets.")
else: print(g(v, sum, memo))
我知道你在10年前就提出了这个问题,但我真的需要知道如何做到这一点,而jbernadas的方法对我来说太难了,所以我用了一个小时在Google上搜索并找到了一个Python库itertools
,它可以完成这项工作!
我希望这能帮助未来的初学者程序员。
你只需要导入库并使用.combinations()
方法,就像这样简单,它会返回具有顺序的集合中所有子集。我的意思是:
对于集合[1, 2, 3, 4]
和长度为3的子集,它不会返回[1, 2, 3][1, 3, 2][2, 3, 1]
,它只会返回[1, 2, 3]
如果你想要集合的所有子集,你可以进行迭代。
import itertools
sequence = [1, 2, 3, 4]
for i in range(len(sequence)):
for j in itertools.combinations(sequence, i):
print(j)
输出结果为:
() (1,) (2,) (3,) (4,) (1, 2) (1, 3) (1, 4) (2, 3) (2, 4) (3, 4) (1, 2, 3) (1, 2, 4) (1, 3, 4) (2, 3, 4)
希望这能帮到你!
for i in b:
if(a(round(n-i,2),b[b.index(i)+1:])):
r.append(i)
return True
return False
l[l.index(i+1):]
,s 变为 s-i。因此,如果我们的函数是 a(l,s),那么我们调用 a(l[l.index(i+1):], s-i)
。如果 i 不是 s 的一部分,则我们必须从 l[l.index(i+1):]
列表中组合出 s。
因此,在两种情况下都是类似的,唯一的区别在于如果 i 是 s 的一部分,则 s = s-i,否则 s = s。if(len(b)==0):
return False
while(b[0]>n):
b.remove(b[0])
if(len(b)==0):
return False
如果l只剩下一个元素,那么它要么是s的一部分,我们返回true;要么不是,我们返回false并循环遍历其他数字。
if(b[0]==n):
r.append(b[0])
return True
if(len(b)==1):
return False
注意循环中如果使用了b,但是b只是我们的列表。我已经尽可能地对其进行了四舍五入,以避免在Python中进行浮点数计算时得到错误的答案。
r=[]
list_of_numbers=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]
list_of_numbers=sorted(list_of_numbers)
list_of_numbers.reverse()
sum_to_be_formed=401.54
def a(n,b):
global r
if(len(b)==0):
return False
while(b[0]>n):
b.remove(b[0])
if(len(b)==0):
return False
if(b[0]==n):
r.append(b[0])
return True
if(len(b)==1):
return False
for i in b:
if(a(round(n-i,2),b[b.index(i)+1:])):
r.append(i)
return True
return False
if(a(sum_to_be_formed,list_of_numbers)):
print(r)
l=[1,6,7,8,10]
and s=22 i.e. s=1+6+7+8
so it goes through like this
1.) [10, 8, 7, 6, 1] 22
i.e. 10 is selected to be part of 22..so s=22-10=12 and l=l.remove(10)
2.) [8, 7, 6, 1] 12
i.e. 8 is selected to be part of 12..so s=12-8=4 and l=l.remove(8)
3.) [7, 6, 1] 4
now 7,6 are removed and 1!=4 so it will return false for this execution where 8 is selected.
4.)[6, 1] 5
i.e. 7 is selected to be part of 12..so s=12-7=5 and l=l.remove(7)
now 6 are removed and 1!=5 so it will return false for this execution where 7 is selected.
5.)[1] 6
i.e. 6 is selected to be part of 12..so s=12-6=6 and l=l.remove(6)
now 1!=6 so it will return false for this execution where 6 is selected.
6.)[] 11
i.e. 1 is selected to be part of 12..so s=12-1=1 and l=l.remove(1)
now l is empty so all the cases for which 10 was a part of s are false and so 10 is not a part of s and we now start with 8 and same cases follow.
7.)[7, 6, 1] 14
8.)[6, 1] 7
9.)[1] 1
l=[61.12,13.11,100.12,12.32,200,60.00,145.34,14.22,100.21,14.77,214.35,145.21,123.56,11.90,200.32,65.43,0.49,132.13,143.21,156.34,11.32,12.34,15.67,17.89,21.23,14.21,12,122,134]
并且
s=2000
我的循环运行了1018次,花费了31毫秒。
而之前的代码循环运行了3415587次,大约需要16秒的时间。
然而,如果不存在解决方案,我的代码会运行超过几分钟,所以我停止了它,而之前的代码只需要大约17毫秒,而且可以处理负数。
因此,我认为有些改进可以进行。
我已经找到了一个答案,它的运行时间复杂度为O(n),空间复杂度约为O(2n),其中n是列表的长度。
这个答案满足以下限制:
列表可以包含重复项,例如[1,1,1,2,3],你想要找到和为2的数对
列表可以包含正整数和负整数
代码如下,后面跟着解释:
def countPairs(k, a):
# List a, sum is k
temp = dict()
count = 0
for iter1 in a:
temp[iter1] = 0
temp[k-iter1] = 0
for iter2 in a:
temp[iter2] += 1
for iter3 in list(temp.keys()):
if iter3 == k / 2 and temp[iter3] > 1:
count += temp[iter3] * (temp[k-iter3] - 1) / 2
elif iter3 == k / 2 and temp[iter3] <= 1:
continue
else:
count += temp[iter3] * temp[k-iter3] / 2
return int(count)
遍历字典,这次是为了找到我们有多少对。我们需要考虑3种情况:
3.1 键恰好是总和的一半,并且此键在列表中出现多次,例如,列表为[1,1,1],总和为2。我们将此特殊条件视为代码所做的操作。
3.2 键恰好是总和的一半,并且此键在列表中仅出现一次,我们跳过此条件。
3.3 对于其他情况,键不是总和的一半,只需将其值与另一个键的值相乘,其中这两个键的总和为给定值。例如,如果总和为6,则将temp [1]和temp [5],temp [2]和temp [4]等相乘...(我没有列出数字为负数的情况,但思路是相同的。)
#!/usr/bin/python2
ylist = [1, 2, 3, 4, 5, 6, 7, 9, 2, 5, 3, -1]
print ylist
target = int(raw_input("enter the target number"))
for i in xrange(len(ylist)):
sno = target-ylist[i]
for j in xrange(i+1, len(ylist)):
if ylist[j] == sno:
print ylist[i], ylist[j]
这段 Python 代码可以实现你的要求,它会打印出所有唯一的数字对,它们的和等于目标变量。
如果目标数字是8,它将打印: 1 7 2 6 3 5 3 5 5 3 6 2 9 -1 5 3