在 Python 中使用递归和 Yield 语句生成幂集

问题描述

我对 Python 有点陌生,正在做编程练习。我编写了以下递归方法来根据 Python 中的输入列表生成 power set。它应该返回一个生成器,该生成生成作为 s 传入的给定列表的幂集。幂集中的每个元素都应该是一个集合。

def gps(s,res=set()):
    if s:
        elem = s.pop()
        gps(s,res)
        res.add(elem)
        gps(s,res)
    else:
        yield res

当我使用 list(gps([1,2])) 调用它时,它给了我 []。正确的结果应该类似于 [set(),{1},{2},{1,2}]

删除yield 语句,添加了两行代码并尝试使用 print 语句来获得此代码,它打印出正确的结果,并且似乎更近了一步:

def gps(s,res)
        s.append(elem)
        res.remove(elem)
    else:
        print(res)

在阅读另一个 Stack Overflow answer 后,我修改了我的函数以使用 yield from,但下面修改后的代码仍然给我错误的结果:

def gps(s,res=set()):
    if s:
        elem = s.pop()
        yield from gps(s,res)
        res.add(elem)
        yield from gps(s,res)
        s.append(elem)
        res.remove(elem)
    else:
        yield res

    

我的方法哪里出了问题?任何提示和澄清将不胜感激。

解决方法

itertools.combinations 可用于获取特定长度列表中元素的所有组合。要获得 powerset,您可以将长度 0 的所有组合与所有长度 1 等组合起来,直到列表的长度

import itertools

s = [1,2]
result = []

for x in range(len(s) + 1): 
    result.extend(map(set,itertools.combinations(s,r=x)))

print(result)  # [set(),{1},{2},{1,2}]

原始输入实际上应该是一个集合或至少具有唯一值,如果列表有重复项,这可能不起作用,因为输入不是“集合”

,

也许你可以试试这段代码,没有任何内置方法。

def subset(nums):
    ans = [[]]

    for n in nums:
        ans += [a+[n] for a in ans]

    return ans


nums = [1,2,3]

print(subset([1,3]))  # [[],[1],[2],[1,2],[3],3],[2,3]] 

或者您仍然更喜欢生成器版本:

def powerset(nums):
    """
    This is a generator version
    """
    if len(nums) <= 1:
        yield nums
        yield []
    else:
        for x in powerset(nums[1:]):
            yield [nums[0]]+ x
            yield x



if __name__ == '__main__':
    nums = [1,3]
    print(list(powerset(nums)))
,

对于递归方法,正如问题的标题所暗示的那样,您可以使函数取出输入序列的第一项,然后从序列其余部分的幂集中递归地产生每个子集,有和没有添加的第一项:

Private Function SubtractCheckedValues(value As Decimal) As Decimal
    Dim checkBoxes = {CheckBox1,CheckBox2,CheckBox3,CheckBox4}
    Dim numericUpDowns = {NumericUpDown1,NumericUpDown2,NumericUpDown3,NumericUpDown4}

    For i = 0 To checkBoxes.GetUpperBound(0)
        If checkBoxes(i).Checked Then
            value -= numericUpDowns(i).Value
        End If
    Next

    Return value
End Function

以便 def powerset(seq): if seq: first,*rest = seq for subset in powerset(rest): yield subset yield {first,*subset} else: yield set() 返回:

list(powerset([1,2]))

然后 [set(),2}] 返回:

list(powerset([1,3]))

由于在上述代码中的 [set(),2},{3},3},{2,3}] 中解包(或切片,在@DanielHao 的答案中的 first,*rest = seq 的情况下)的时间复杂度是 O(n),您可以通过首先从输入序列创建迭代器来将步骤的时间复杂度提高到 O(1),以便您可以在每个递归级别以恒定时间从序列中获取下一项:

nums[1:]