Python子集总和

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23087820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 02:13:20  来源:igfitidea点击:

Python Subset Sum

pythonsubset-sum

提问by Chase McCoy

I am trying to write a function that will not only determine whether the sum of a subset of a set adds to a desired target number, but also to print the subset that is the solution.

我正在尝试编写一个函数,该函数不仅可以确定集合子集的总和是否与所需的目标数相加,而且还可以打印作为解决方案的子集。

Here is my code for finding whether a subset exists:

这是我用于查找子集是否存在的代码:

def subsetsum(array,num):

    if num == 0 or num < 1:
        return False
    elif len(array) == 0:
        return False
    else:
        if array[0] == num:
            return True
        else:
            return subsetsum(array[1:],(num - array[0])) or subsetsum(array[1:],num)

How can I modify this to record the subset itself so that I can print it? Thanks in advance!

如何修改它以记录子集本身以便我可以打印它?提前致谢!

采纳答案by Samy Arous

Based on your solution:

根据您的解决方案:

def subsetsum(array,num):

    if num == 0 or num < 1:
        return None
    elif len(array) == 0:
        return None
    else:
        if array[0] == num:
            return [array[0]]
        else:
            with_v = subsetsum(array[1:],(num - array[0])) 
            if with_v:
                return [array[0]] + with_v
            else:
                return subsetsum(array[1:],num)

回答by jonrsharpe

You could change your approach to do that more easily, something like:

你可以改变你的方法来更容易地做到这一点,比如:

def subsetsum(array, num):
    if sum(array) == num:
        return array
    if len(array) > 1:
        for subset in (array[:-1], array[1:]):
            result = subsetsum(subset, num)
            if result is not None:
                return result

This will return either a valid subset or None.

这将返回一个有效的子集或None.

回答by Reut Sharabani

Thought I'll throw another solution into the mix.

以为我会加入另一个解决方案。

We can map each selection of a subset of the list to a (0-padded) binary number, where a 0 means not taking the member in the corresponsing position in the list, and 1 means taking it.

我们可以将列表子集的每个选择映射到一个(0 填充的)二进制数,其中 0 表示不占用列表中相应位置的成员,1 表示占用它。

So masking [1, 2, 3, 4]with 0101creates the sub-list [2, 4].

因此屏蔽[1, 2, 3, 4]with0101创建子列表[2, 4]

So, by generating all 0-padded binary numbers in the range between 0 and 2^LENGTH_OF_LIST, we can iterate all selections. If we use these sub-list selections as masks and sum the selection - we can know the answer.

因此,通过生成 0 到 2^LENGTH_OF_LIST 范围内的所有 0 填充二进制数,我们可以迭代所有选择。如果我们使用这些子列表选择作为掩码并对选择求和 - 我们可以知道答案。

This is how it's done:

这是它的完成方式:

#!/usr/bin/env python

# use a binary number (represented as string) as a mask
def mask(lst, m):
    # pad number to create a valid selection mask 
    # according to definition in the solution laid out 
    m = m.zfill(len(lst))
    return map(lambda x: x[0], filter(lambda x: x[1] != '0', zip(lst, m)))

def subset_sum(lst, target):
    # there are 2^n binary numbers with length of the original list
    for i in xrange(2**len(lst)):
        # create the pick corresponsing to current number
        pick = mask(lst, bin(i)[2:])
        if sum(pick) == target:
            return pick
    return False


print subset_sum([1,2,3,4,5], 7)

Output:

输出:

[3, 4]

To return all possibilities we can use a generator instead (the only changes are in subset_sum, using yieldinstead of returnand removing return Falseguard):

为了返回所有的可能性,我们可以使用一个生成器来代替(唯一的变化是在subset_sum,使用yield而不是return和移除return False守卫):

#!/usr/bin/env python

# use a binary number (represented as string) as a mask
def mask(lst, m):
    # pad number to create a valid selection mask 
    # according to definition in the solution laid out 
    m = m.zfill(len(lst))
    return map(lambda x: x[0], filter(lambda x: x[1] != '0', zip(lst, m)))

def subset_sum(lst, target):
    # there are 2^n binary numbers with length of the original list
    for i in xrange(2**len(lst)):
        # create the pick corresponsing to current number
        pick = mask(lst, bin(i)[2:])
        if sum(pick) == target:
            yield pick

# use 'list' to unpack the generator
print list(subset_sum([1,2,3,4,5], 7))

Output:

输出:

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


Note:While not padding the mask with zeros may work as well, as it will simply select members of the original list in a reverse order - I haven't checked it and didn't use it.

注意:虽然不用零填充掩码也可能有效,因为它只会以相反的顺序选择原始列表的成员 - 我没有检查它也没有使用它。

I didn't use it since it's less obvious (to me) what's going on with such trenary-like mask (1, 0 or nothing) and I rather have everything well defined.

我没有使用它,因为它不太明显(对我来说)这种类似 trenary 的掩码(1、0 或什么都没有)发生了什么,我宁愿让一切都得到很好的定义。

回答by harry

Slightly modified version of Samy's answer to print all possible combinations.

Samy 的答案的稍微修改版本以打印所有可能的组合。

def subset(array, num):
    result = []
    def find(arr, num, path=()):
        if not arr:
            return
        if arr[0] == num:
            result.append(path + (arr[0],))
        else:
            find(arr[1:], num - arr[0], path + (arr[0],))
            find(arr[1:], num, path)
    find(array, num)
    return result