在Python中查找与给定值求和的子数组

在Python中查找与给定值求和的子数组,python,recursion,sub-array,Python,Recursion,Sub Array,如果给我一个子数组[1,2,3,4]和一个值8。我想返回子数组[1,3,4]。我的代码中有一个bug,我不知道如何修复它,因为我是递归新手。下面是我的Python代码。我得到了要打印的值[3,4],这显然不是正确答案。如何获取数组中的第一个元素 def main(): s = 0 a = [1,2,3,4] # given array sa = [] # sub-array w = 8 # given weight d = False d, sa

如果给我一个子数组[1,2,3,4]和一个值8。我想返回子数组[1,3,4]。我的代码中有一个bug,我不知道如何修复它,因为我是递归新手。下面是我的Python代码。我得到了要打印的值[3,4],这显然不是正确答案。如何获取数组中的第一个元素

def main():
    s = 0
    a = [1,2,3,4] # given array
    sa = [] # sub-array
    w = 8 # given weight
    d = False
    d, sa = checkForWeight(a,w,s,d,sa)
    print sa

def checkForWeight(a,w,s,d,sa):
    l = len(a)
    s += a[0]
    sa.append(a[0])
    if s == w:
        d = True
        return d, sa
    else:
        try:
            d, sa = checkForWeight(a[1:],w,s,d,sa)
            if d != True:
                d, sa = checkForWeight(a[2:],w,s,d,sa)
            else:
                return d, sa
        except:
            sa = [] # i put this here because I want to erase the incorrect array
    return d, sa

您是否需要与总和匹配的任何子数组?还是所有子阵列?(或者最短的,或者最长的?)正确的答案在很大程度上取决于这一点

顺便说一句,这是背包问题的一个变种:

此外,您的递归策略在复杂性上似乎是阶乘的。(如果是针对代码测试,仅此一项就可能使申请人失败。)我强烈建议采用动态编程方法

编辑

如果你需要所有可能的答案,你正在研究一个NP问题。我建议将重点放在易于实施/维护上,而不是绝对的性能上,以展示您的技能。例如:

import itertools

def find_all_subsets_that_sum(elements, total):
  for i in range(len(elements)):
    for possible in itertools.combinations(elements, i+1):
      if sum(possible)==total:
        yield possible

print list(find_all_subsets_that_sum([1,2,3,4,5,6,7,8,9,10], 10))
不是绝对最快的(你可以在一个自滚递归解决方案中做很多修剪),但它将是与你提出的任何更复杂的解决方案相同的大O。(所有的解决方案都将由O(n选择n/2)主导。)很少有面试候选人会回答如下问题:

这并没有可能的那么快,但它离最快的速度只有几步之遥,而且很可能是开发人员在实施和维护时的最佳ROI。当然,除非我们正在解析的数据集是巨大的,在这种情况下,我建议放宽要求,返回一些启发式的解决方案,这些解决方案可以用O(n^2)动态规划解决方案计算。”


你可以用它脱颖而出。

你需要任何与你的总和相匹配的子数组吗?或者所有子数组?(或者最短的,或者最长的?)正确的答案将高度依赖于此

顺便说一句,这是背包问题的一个变种:

此外,您的递归策略在复杂性上似乎是阶乘的(如果是针对代码测试,那么申请人可能仅此一项就失败了。)我强烈建议采用动态编程方法

编辑

如果您需要所有可能的解决方案,您将面临一个NP问题。我建议您将重点放在易于实施/维护上,而不是绝对性能上,以展示您的技能。例如:

import itertools

def find_all_subsets_that_sum(elements, total):
  for i in range(len(elements)):
    for possible in itertools.combinations(elements, i+1):
      if sum(possible)==total:
        yield possible

print list(find_all_subsets_that_sum([1,2,3,4,5,6,7,8,9,10], 10))
不是绝对最快的(你可以在一个自滚递归解决方案中进行大量修剪),但它将与你提出的任何更复杂的解决方案相同。(所有解决方案都将由O(n选择n/2)控制。)很少有面试应聘者会这样回答:

这不是最快的速度,但它离最快的速度只有几步之遥,而且很可能是开发人员在实施和维护时的最佳ROI。当然,除非我们解析的数据集很大,在这种情况下,我建议放松要求,返回一些启发性的解决方案可以使用O(n^2)动态规划解决方案进行计算。”


您可以使用它脱颖而出。

直接的问题是,您在函数顶部向sa添加了一个[0],但随后会使用子调用的返回值来销毁它。要对此进行修补,请在返回最终结果之前添加一个子句:

    except:
        sa = [] # i put this here because I want to erase the incorrect array
if d:
    sa = [a[0]] + sa
print "Leave: drop", d,sa
return d, sa
我确实建议您遵循评论中的建议:不要到处传递太多东西,而是专注于局部控制部分解决方案

尝试两种解决方案:使用和不使用当前元素。您的递归调用将如下所示:

sa = checkForWeight(a[1:], w)         # Solutions without first element
    -- and --
sa = checkForWeight(a[1:], w-a[0])    # Solutions using first element

You don't have to return a success flag; if **sa** is None or empty, the call failed to find a solution.  If it succeeded, in the **w-a[0]** call, then you also need to prepend each solution in **sa** with **a[0]**.

这会让您移动吗?

直接的问题是,您会在函数顶部向sa追加一个[0],但稍后会使用子调用的返回值来销毁它。要对此进行修补,请在返回最终结果之前添加一个子句:

    except:
        sa = [] # i put this here because I want to erase the incorrect array
if d:
    sa = [a[0]] + sa
print "Leave: drop", d,sa
return d, sa
我确实建议您遵循评论中的建议:不要到处传递太多东西,而是专注于局部控制部分解决方案

尝试两种解决方案:使用和不使用当前元素。您的递归调用将如下所示:

sa = checkForWeight(a[1:], w)         # Solutions without first element
    -- and --
sa = checkForWeight(a[1:], w-a[0])    # Solutions using first element

You don't have to return a success flag; if **sa** is None or empty, the call failed to find a solution.  If it succeeded, in the **w-a[0]** call, then you also need to prepend each solution in **sa** with **a[0]**.

这能让你有所行动吗?

我提出了一个有效的递归解决方案,希望它能帮助你:

def main():
    success, solution = WeightChecker((1,2,3,4)).check(8)
    print solution

class WeightChecker(object):
    def __init__(self, to_check):
        self._to_check = to_check
    def check(self, weight):
        return self._check((), 0, weight)
    def _check(self, current_solution, index_to_check, remaining_weight):
        if remaining_weight == 0:
            return True, current_solution
        if index_to_check == len(self._to_check):
            return False, ()
        current_check = self._to_check[index_to_check]
        success, solution = self._check(current_solution + (current_check, ), index_to_check + 1, remaining_weight - current_check)
        if not success:
            success, solution = self._check(current_solution, index_to_check + 1, remaining_weight)
        return success, solution

(正如keredson所建议的那样,动态规划方法更好)

我提出了一个有效的递归解决方案,希望它能帮助:

def main():
    success, solution = WeightChecker((1,2,3,4)).check(8)
    print solution

class WeightChecker(object):
    def __init__(self, to_check):
        self._to_check = to_check
    def check(self, weight):
        return self._check((), 0, weight)
    def _check(self, current_solution, index_to_check, remaining_weight):
        if remaining_weight == 0:
            return True, current_solution
        if index_to_check == len(self._to_check):
            return False, ()
        current_check = self._to_check[index_to_check]
        success, solution = self._check(current_solution + (current_check, ), index_to_check + 1, remaining_weight - current_check)
        if not success:
            success, solution = self._check(current_solution, index_to_check + 1, remaining_weight)
        return success, solution

(正如keredson所建议的,动态规划方法更好)

与您的问题无关:使用值
True
False
(布尔值)而不是字符串
'True'
'False'
。也没有直接关系:您总是使用[0]作为解决方案的一部分。如果解决方案不包括它怎么办?你是对的!当我第一次调用它时,我应该将它添加到我的函数中。除了你的错误之外,还有一些评论。如果要为函数提供许多参数,最好使用带有类或实例变量的类,这样就不会有那么多参数。如果将变量命名为有意义的东西,代码就会更清晰,例如调用“w”权重,而不是在其旁边添加注释;checkForWeight的第一个局部变量尤其如此。最好避免使用小写字母“l”和大写字母“o”,因为它们很容易与1和0混淆。此外,将可变类型(如列表)传递给函数,然后再对其进行更改在这里非常容易混淆,并且在您的实现中不需要。与您的问题无关:使用值
True
False
(布尔值)而不是字符串
'True'
'False'
。也没有直接关系:您总是使用[0]作为解决方案的一部分。如果解决方案不包括它怎么办?你是对的!我应该把它加到m中