如何递归地查找列表的所有部分解决方案

时间:2015-12-09 10:11:48

标签: python recursion

我有一个最大重量容量的盒子。我得到了一份我应该放在盒子里的物品的重量清单。我需要所有达到或尽可能接近最大容量的解决方案。我的意思是,我不应该在不超过最大容量的情况下为任何部分解决方案添加另一个项目。

如果给出以下权重列表:

[1,2,3,4,5]

如果最大容量是9,解决方案应该是(我可能错过了一个,但你明白了):

[[4,3,2], [5,3,1], [5,4], [3,2,1], [4,2,1], [4,3,1], [5,3]]

这是我的递归算法,我想我已经接近但我无法弄清楚如何修复它。

def findSubset(alist, maxim):
    if maxim <= 0:
        return [[]]
    if len(alist) == 0:
        return []
    alist2 = alist[1:]
    include = findSubset(alist2, maxim-alist[0])
    for s in include:
        s.append(alist[0])
    return include + findSubset(alist2, maxim)

当前输出为:

[[4, 3, 2, 1], [5, 3, 2, 1], [5, 4, 2, 1], [5, 4, 3, 1], [5, 3, 1], [5, 4, 1], [4, 3, 2], [5, 3, 2], [5, 4, 2], [5, 4, 3], [5, 4]]

3 个答案:

答案 0 :(得分:1)

我的建议是迭代列表中的所有元素,同时构建可能的解决方案列表

  • 如果元素是&lt; maxim,递归到maxim - elt的以下元素的子列表中,并且对于结果的每个元素,将元素追加到它并将所有元素追加到结果列表中
  • 如果元素是== maxim,则将包含该元素的单例列表添加到结果列表中

代码是:

def findSubset(alist, maxim):
    res = []
    if maxim <= 0:
        return [[]]
    if len(alist) == 0:
        return []
    for i, elt in enumerate(alist):
        if elt < maxim:
            res.extend([ [elt] + l for l in findSubset(alist[i+1:], maxim-elt)])
        elif elt == maxim:
            res.append([elt])
    return res

它给出了

>>> findSubset(lst, 9)
[[1, 3, 5], [2, 3, 4], [4, 5]]

以上代码仅提供准确的解决方案。如果没有确切的解决方案,应该扩展它以提供最佳的解决方案:

def findApproachSubset(alist, maxim):
    for i in range(maxim, 0, -1):
        res = findSubset(alist, i)
        if len(res) > 0:
            return res
    return []

例如:

>>> findSubset([1, 4, 5, 6], 8)
[]
>>> findApproachSubset([1, 4, 5, 6], 8)
[[1, 6]]

因为这里最好的解决方案是7而不是8。

答案 1 :(得分:0)

这是使用Haskell编写的。由于这似乎是家庭作业,所以没有必要给你完整的答案。

我们使用三个单独的函数,函数f可以递归,因为它只是map

xs = [1, 2, 3, 4, 5]

perms [] = []
perms xs = xs : perms (tail xs)

您需要知道的是:表示cons,即1:[2] = [1,2] 在python中,我们可以把它写成:

def perms(xs):
    if xs:
        return [xs] + perms(xs[1:])
    else:
        return []

所以perms xs只是[[1,2,3,4,5],[2,3,4,5],[3,4,5],[4,5],[5]]

takeWhileLessThan _ []     = []
takeWhileLessThan n (x:xs) =
  if n-x < 0 then [] else (x : takeWhileLessThan (n-x) xs)

takeWhileLessThan使用递归。它在单个列表上运行,跟踪当前总和。

f n xs = map (takeWhileLessThan n) (perms xs)
> [[1,2,3],[2,3,4],[3,4],[4,5],[5]]

final函数将递归函数映射到列表列表中。如果你想要所有的值并希望它是递归的,那就写下另一个函数......

如果允许循环,那么这种方法可以正常工作。唯一的递归是主要功能。

def f(n, xs, solutions):
    if xs:
        m = n
        ys = []
        for x in xs:
            if (m-x) < 0:
                break
            else:
                ys.append(x)
                m = m-x
        solutions.append(ys)
        return f(n, xs[1:], solutions)
    else:
        return solutions

给出:

>>> f(9, [1,2,3,4,5], [])
[[1, 2, 3], [2, 3, 4], [3, 4], [4, 5], [5]]

答案 2 :(得分:0)

def recursive_function(current_node: int, nodes_left: list, maximum: int, trace: list) -> list:
    sum_so_far = sum(trace)
    if current_node is not None:
        trace.append(current_node)       
    else:
        current_node = 0
    if sum_so_far + current_node > maximum:
        # That means, that current trace is already to big 
        # no need to search further this path
        return False
    elif sum_so_far + current_node == maximum:
        # Bingo! The perfect set of nodes!
        # No need to look further this path
        return {tuple(sorted(trace))}
    else:
        # We still haven't reached maximum value
        # let's check if we can pack some more nodes to trace
        results = set()
        for node in nodes_left:
            nodes_copy = nodes_left.copy()
            nodes_copy.remove(node)
            traces = recursive_function(node, nodes_copy, maximum, trace.copy())
            if traces:
                # This path gave us some legitimate results, we collect them
                results.update(traces)
        if results:
            # At least one possible path gave us results, we pass them on
            return results
    # There was nothing left in nodes_left that we could add and not
    # cross the maximum limit. The path leading to this moment fits
    # our requirements.
    return {tuple(sorted(trace))}


def findSubset(alist, maxim) -> list:
    results = recursive_function(None, alist, maxim, [])
    return results

为什么我这样做{元组(已排序(跟踪))}?因此,我不会在记忆中保留相同结果的不同排列。

时间成本:n!

内存成本:n