【问题标题】:Function that find combination of values that sums to a given number查找总和为给定数字的值组合的函数
【发布时间】:2019-10-17 20:27:06
【问题描述】:

这篇文章Finding Combinations to the provided Sum value 介绍了函数subsets_with_sum()。它在数组中找到总和等于给定值的值组合。但是由于帖子已经超过6年了,所以我发了这个帖子来问:这个功能是如何工作的?它在做什么?

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)

【问题讨论】:

    标签: python


    【解决方案1】:

    首先,我将添加一些换行符以使其更易于阅读:

    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 定义了一个函数并在最后一个return 语句中调用它一次。第一个元素是您从中采样值的列表,第二个是目标总和,第三个是告诉函数是否可以多次使用 lst 中的单个元素的参数。

    因为_a 是在subsets_with_sum 中定义的,所以它封闭 将值传递给subsets_with_sum - 这很重要,lst 对于@ 的每个递归调用都是相同的987654329@。要注意的第二件事是l 是一个值列表,而r 是一个列表列表。重要的是,l 是每个_a 调用的不同对象(这是由于l + [lst[u]] 导致l 的副本与[lst[u]] 连接)。但是,r 表示每个_a 调用的相同 对象-r.append(l) 会对其进行修改。

    我将从with_replacement == True 开始,因为我觉得它更简单。第一次调用_a 传递0, [], [], target。然后它检查是否t == sum(l) 并将其附加到r,因此r 的唯一元素将是总和为t(或target)的列表。如果sum(l) 返回None,则该递归分支被丢弃。如果l 的元素总和小于target,那么对于lst 中的每个 元素,该元素将附加到l副本并使用该列表调用 _a。这是打印出步骤的函数的修正版本,因此我们可以检查发生了什么:

    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 + 1 级别的左列 (l)。此外,请注意[3] - 最后一行c == 0 没有超过elif t &lt; sum(l),因此它不会被打印。类似地,c == 1 处的 [2] 被附加到 r,但仍然分支继续到下一个级别,因为 lst 的一个或多个元素可能等于 0,因此将它们附加到 [2] 可能结果是另一个列表,总和为target

    另外,请注意对于特定级别c 的每个列表,其最后一个元素是lst[u] 将出现len(lst) - u-次在下一个级别c + 1,因为这是列表的组合数每个lst[z],其中z &gt;= u

    那么with_replacement == False(默认情况)会发生什么?好吧,在这种情况下,每次将lst[u] 添加到lsum(l) &lt; t,以便l 的特定实例继续到下一个递归级别,只有lst[z] 可以添加到列表中,其中@987654383 @,因为 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 &gt; 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)

    【讨论】:

    • 嗨,这个解释对我来说已经很清楚了,但是如果我想获取值的索引而不是值怎么办?有没有办法修改函数以返回组合的索引?
    • 没关系,我只是将第 4 行和第 10 行更改如下:if t == sum([lst[index] for index in l])_a(u + x, l + [u], r, t)
    猜你喜欢
    • 1970-01-01
    • 2017-02-20
    • 1970-01-01
    • 1970-01-01
    • 2015-10-31
    • 2016-04-03
    • 1970-01-01
    • 2020-04-06
    • 1970-01-01
    相关资源
    最近更新 更多