繁体   English   中英

Function 找到总和为给定数字的值组合

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM