递归子集求和函数

时间:2016-10-15 15:30:11

标签: python subset-sum

我们的教授在递归时为我们的类共享了以下Python代码。这是'子集和'问题的解决方案。

我一遍又一遍地阅读它并尝试检查它并使用在线工具一步一步地遵循参数,但我根本没有得到它。

我理解代码检查列表L的子集是否有可能使总和为0,但我不明白该函数究竟是如何检查总和是否实际为0.简单地说:我没有看到任何地方使用的求和函数,所以代码如何知道子集的元素之和为0。

def possible(L,som=0,used=False):
    if L==[]:
        return (som==0) and used
    else:
        return (possible(L[1:],som,used) or possible(L[1:],som-L[0],True))

我知道有一些关于subset-sum与Python结合的问题,我已经看过一个类似的函数发布了,但是没有解释它(至少不是我理解的那个)。

1 个答案:

答案 0 :(得分:1)

摘要

def possible(L,som=0,used=False):
    if the list is empty:
        return (is our running sum 0 and have we used at least one element?)
    else:
        return (what if we ignore the first element?) or \
               (what if we use the first element?)

基本理念

此代码的基础是L中的每个元素必须在任何可能的子集中;没有第三种选择。这非常有用,因为我们确切知道我们应该为这两种情况做些什么。我们要么将元素包含在总和中,要么我们不包含。该总和是逐步计算的,存储在som中。不使用变量名sum,因为sum已经是Python函数(您似乎已经知道了)。该代码仍然可以在som中重命名为sum,但这是错误的编码实践。这两种情况由下面显示的两个递归调用表示:

案例1:

此调用测试子集中包含第一个元素的情况。

possible(L[1:],som,used)

我们使用L[1:]从进一步检查中排除第一个元素。不使用第一个元素,因此它不会以任何方式更改总和。因此,不会对som进行任何更改,而是按原样传递。最后,used没有改变。我会在帖子中进一步说明。

案例2:

此调用测试子集中包含第一个元素 的情况。

possible(L[1:],som-L[0],True)

和以前一样,我们不想再次检查第一个元素,所以我们在L[1:]上递归。我们 使用L[0],因此必须以某种方式更改当前总和(som)。你的教授选择减去它,但如果他愿意添加它,它的工作方式相同。 used现在变为True

那么这个used废话是什么,为什么我们在列表为空时关心它呢?

一个主要问题,空子集的所有元素的总和是多少?就个人而言,我的思绪首先转到0。但这是一个问题,因为空集是每个列表的子集。这意味着,如果我们认为空子集的总和为0,那么每个可能的任何一组,数字与否,都会有一个总和为0的子集。这将产生更容易的实现:

def possible(L):
    return True

但这根本没用。因此,我们应该只考虑使用某个元素的情况,这是used变量的来源。只要包含任何元素,它就会设置为True,当我们得到时到基础案例,用于过滤掉空集案例。