翻译递归解决方案以查找列表的所有子集

时间:2015-12-09 20:09:44

标签: python algorithm recursion

我想知道是否有一种相对简单的方法可以迭代地查找列表的所有子集。使用递归,这很容易完成,非常简洁......

def partial(alist, pos, chosen):
    if pos == len(alist):
        return [chosen]
    else:
        return partial(a list, pos+1, chosen) \
               + partial(alist, pos+1, chosen + [alist[pos]])

这将返回包含列表所有子集的列表列表。

有没有办法迭代地做这样的事情而不会过于复杂,或者递归是最好的方法?一些伪代码或解释迭代执行此操作的方法会有所帮助。我知道itertools是有帮助的,但如果可能的话,我想要一个解决方案而不需要它。

3 个答案:

答案 0 :(得分:4)

因为有2**n个子集(n是列表的长度)。您可以创建从02**(n-1)的计数器。然后通过在计数器二进制形式中添加相应位设置为1的元素,在每次迭代中创建一个列表(这是一个子集)。

counter = 5
binary_form = 101
you create a subset using first and third element of the original list

counter = 7
binary_form = 111
you create a subset using first, second and third element of the original list

可以像这样实现这个,

result = [] 
A = [1,2,3,4]
for i in range(0,2**len(A)):
    binary = bin(i)[2:] 
    binary = '0'*(len(A)-len(binary)) + binary
    subset = [ A[i] for i,x in enumerate(binary) if x=='1' ]
    print binary,subset
    result.append(subset)
print result

输出

0000 []
0001 [4]
0010 [3]
0011 [3, 4]
0100 [2]
0101 [2, 4]
0110 [2, 3]
0111 [2, 3, 4]
1000 [1]
1001 [1, 4]
1010 [1, 3]
1011 [1, 3, 4]
1100 [1, 2]
1101 [1, 2, 4]
1110 [1, 2, 3]
1111 [1, 2, 3, 4]

但正如评论和其他答案中所提到的,最好不要制作二进制字符串。如果要检查是否设置了某个位,可以将1移动所需的量,然后按位数和该数字进行。例如,要检查third中是否设置了423位,您可以执行此操作:

number = 423
if number & 1<<3:
    print 'this bit is set'

答案 1 :(得分:2)

正如@ sudomakeinstall2所提到的,您可以使用02**(n-1)的计数器来遍历列表,并将其用作掩码从alist中选择值。

def partial(alist):
    result = []
    for i in range(2 ** len(alist)): # iterate over all results
        tmp = []
        for j in range(len(alist)):
            if (2 ** j) & i:         # use bit mask to pick the value
                tmp.append(alist[j])
        result.append(tmp)

    return result

结果可能非常大,您可能想要创建一个生成器以便懒惰地评估。

使用列表理解和生成器:

def partial(alist):
    for i in range(2 ** len(alist)):
        yield [alist[j] for j in range(len(alist)) if 2 ** j & i]

您可以使用

进行调用
for i in partial([1, 2, 3]):
    print(i)

result = list(partial([1, 2, 3]))

答案 2 :(得分:1)

基本上构建子集列表与构建大小1到输入长度的所有组合的列表相同。因此,一个解决方案涉及使用itertools

def sortable(column, title = nil)
      if column == 'category' then
        title ||= 'Category'
        column == 'category_id'
        direction = (column == params[:sort] && params[:direction] == "asc") ? "desc" : "asc"
        link_to title, :sort => 'category_id', :direction => direction
    else
        title ||= column.titleize
        direction = (column == params[:sort] && params[:direction] == "asc") ? "desc" : "asc"
        link_to title, :sort => column, :direction => direction
   end
  end

在此处查看此操作:http://ideone.com/geTdUS

在这种情况下,我避免过多使用递归的主要原因是因为你可能最终会进行大量的递归调用,并且Python中的最大递归深度可能不够深。这就是说我不完全确定这个itertools解决方案的输入大小有多大。