![](/img/trans.png)
[英]Given a list of ranges, find all combination for these ranges that sums upto k
[英]Function that find combination of values that sums to a given number
这篇文章( 查找提供的 Sum 值的组合)介绍了 function subsets_with_sum()
。 它在数组中找到总和等于给定值的值组合。 但是由于帖子已经超过6年了,所以我发了这个帖子来问:这个function是如何工作的? 它在做什么?
def subsets_with_sum(lst, target, with_replacement=False):
x = 0 if with_replacement else 1
def _a(idx, l, r, t):
if t == sum(l): r.append(l)
elif t < sum(l): return
for u in range(idx, len(lst)):
_a(u + x, l + [lst[u]], r, t)
return r
return _a(0, [], [], target)
首先,我将添加一些换行符以使其更易于阅读:
def subsets_with_sum(lst, target, with_replacement=False):
x = 0 if with_replacement else 1
def _a(idx, l, r, t):
if t == sum(l):
r.append(l)
elif t < sum(l):
return
for u in range(idx, len(lst)):
_a(u + x, l + [lst[u]], r, t)
return r
return _a(0, [], [], target)
您会注意到的第一件事是, subsets_with_sum
定义了一个递归 function 并在最后一个return
语句中调用它一次。 第一个元素是您从中采样值的列表,第二个是目标总和,第三个是一个参数,它告诉 function 是否可以多次使用来自lst
的单个元素。
因为_a
是在subsets_with_sum
中定义的,所以它包含在传递给subsets_with_sum
的值中 - 这很重要, lst
对于_a
的每个递归调用都是相同的。 要注意的第二件事是l
是一个值列表,而r
是一个列表列表。
我将从with_replacement == True
开始,因为我发现它更简单。 第一次调用_a
传递0, [], [], target
。 然后它检查t == sum(l)
并将其附加到r
,因此r
的唯一元素将是总和为t
(或target
)的列表。 如果sum(l)
通过返回None
丢弃该递归分支。 如果l
的元素总和的值小于target
,则对于lst
中的每个元素,该元素将附加到l
的副本,并使用该列表调用_a
。 这是 function 的修订版本,它打印出这些步骤,因此我们可以检查发生了什么:
def subsets_with_sum(lst, target, with_replacement=False):
x = 0 if with_replacement else 1
def _a(idx, l, r, t, c):
if t == sum(l):
r.append(l)
elif t < sum(l):
return
for u in range(idx, len(lst)):
print("{}. l = {} and l + [lst[u]] = {}".format(c, l, l + [lst[u]]))
_a(u + x, l + [lst[u]], r, t, c+1)
return r
return _a(0, [], [], target, 0)
调用subsets_with_sum([1,2,3], 2, True)
会打印以下内容(我重新排序并稍微分开打印的行,以便它们更易于阅读):
0. l = [] and l + [lst[u]] = [1]
0. l = [] and l + [lst[u]] = [2]
0. l = [] and l + [lst[u]] = [3]
1. l = [1] and l + [lst[u]] = [1, 1]
1. l = [2] and l + [lst[u]] = [2, 2]
1. l = [2] and l + [lst[u]] = [2, 3]
1. l = [1] and l + [lst[u]] = [1, 2]
1. l = [1] and l + [lst[u]] = [1, 3]
2. l = [1, 1] and l + [lst[u]] = [1, 1, 1]
2. l = [1, 1] and l + [lst[u]] = [1, 1, 2]
2. l = [1, 1] and l + [lst[u]] = [1, 1, 3]
您可以看到 c 级别的右列 ( l + [lst[u]]
) 等于c
c + 1
级别的左列 ( l
)。 此外,请注意[3]
- c == 0
的最后一行没有超过elif t < sum(l)
,因此它不会被打印。 同样, [2]
在c == 1
被附加到r
,但仍然分支继续到下一个级别,因为lst
的一个或多个元素可能等于 0,因此将它们附加到[2]
可能会导致另一个总和为target
的列表。
另外,请注意对于特定级别的每个列表c
,其最后一个元素是lst[u]
将在下一个级别出现len(lst) - u
c + 1
,因为这是每个列表的组合数lst[z]
,其中z >= u
。
那么with_replacement == False
(默认情况)会发生什么? 好吧,在这种情况下,每次将lst[u]
添加到l
并且sum(l) < t
,因此l
的特定实例继续到下一个递归级别,只有lst[z]
可以添加到列表中,其中z > u
,因为u + 1
被传递给相应的_a
调用。 让我们看看当我们调用subsets_with_sum([1,2,3], 2, False)
时会发生什么:
0. l = [] and l + [lst[u]] = [1]
0. l = [] and l + [lst[u]] = [3]
0. l = [] and l + [lst[u]] = [2]
1. l = [1] and l + [lst[u]] = [1, 1]
1. l = [1] and l + [lst[u]] = [1, 2]
1. l = [1] and l + [lst[u]] = [1, 3]
1. l = [2] and l + [lst[u]] = [2, 2]
1. l = [2] and l + [lst[u]] = [2, 3]
2. l = [1, 1] and l + [lst[u]] = [1, 1, 1]
2. l = [1, 1] and l + [lst[u]] = [1, 1, 2]
2. l = [1, 1] and l + [lst[u]] = [1, 1, 3]
现在观察,对于某个级别的每个列表c
,其最后一个元素是lst[u]
将在下一个级别出现len(lst) - u - 1
c + 1
,因为这是列表的组合数每个lst[z]
,其中z > u
。 将此与上面分析的with_replacement=True
进行比较。
我建议你多玩一些,直到你更好地理解它。 不管替换如何,重要的是要意识到 return 语句总是返回给调用者。 _a
的第一次调用返回到subsets_with_sum
,然后返回到调用者(大概是你或其他一些函数)。 对_a
_a
的先前实例,这些调用只是丢弃返回的值(或者更准确地说,不费心对它做任何事情)。 您取回正确r
的原因是因为它的行为有点像全局值 - 每个找到解决方案的_a
调用,将其添加到相同的r
。
最后, subsets_with_sum
并不像我期望的那样工作,它基于其所需的功能。 尝试以下两个调用: subsets_with_sum([2,2,1], 5, True)
和subsets_with_sum([2,2,1,1,1], 5, False)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.