算法-求集合所有子集

Posted 詩和遠方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法-求集合所有子集相关的知识,希望对你有一定的参考价值。

问题:求集合所有子集。
下面分别给出两种思路的递归和非递归解法,用python实现。

方法一

a的子集可以分为两部分:

  1. 不含a[0]的所有子集
  2. 含a[0]的所有子集(等于将不含a[0]的所有子集都加上a[0])

举例,如下图,[1,2]的子集就是将[1]的子集复制一份再加上2:

总结:a[0:n]所有子集 = a[1:n]的所有子集(不含a[0]) + a[0] + a[1:n]的所有子集(含a[0])

举例:
subset([1,2]) = subset([2]) + 1 + subset([2])
              = [],[2] + 1 + ([],[2]) 
              = [],[2] + [1],[1,2] 
              = [],[1],[2],[1,2] 

递归版本:

def subset_sub(ls, index, max_index, result):
    """put all subsets of ls[index:max_index] to result"""
    if index == max_index: # if last one, add it to result 
        result.append(ls[index:]) 
        return
    subset_sub(ls, index + 1, max_index, result) # subset of ls[index + 1:]
    for x in list(result): # add ls[index] to every element in result
        y = list(x) # another list
        y.append(ls[index])
        result.append(y)

def subset(ls):
    if len(ls) == 0:
        return [[]]
    result = [[]]
    subset_sub(ls, 0, len(ls)-1, result)
    return result

if __name__ == "__main__":
    ls = [1, 2, 3]
    result = subset(ls)
    for r in result:
        print(r)
结果:
	[]
	[3]
	[2]
	[3, 2]
	[1]
	[3, 1]
	[2, 1]
	[3, 2, 1]

非递归版本:

def subset(ls):
    result = [[]] # empty set
    if len(ls) == 0:
        return result
    for i in ls:
        for j in range(len(result)): # add to current result with each item in ls
            temp_ls = list(result[j])
            temp_ls.append(i)
            result.append(temp_ls)
    return result

if __name__ == "__main__":
    ls = [1,2,3]
    for r in subset(ls):
        print(r)
结果:
	[]
	[1]
	[2]
	[1, 2]
	[3]
	[1, 3]
	[2, 3]
	[1, 2, 3]

方法二

每个元素的选或不选,分别用0和1来表示,那么长度为3的集合的所有子集,相当于根据000,001,010,011 … 111这样的掩码序列,输出对应的元素,000为空集,111为全集,所以问题转化为了生成所有掩码序列。

递归版本

根据如下规则:

  • MaskCode(a[0:n]) = (1 + MaskCode(a[1:n]) + (0 + MaskCode(a[1:n])

递归依次设置每一位的0或1的状态,代码如下:

def subset_mask(index, max_index, working_set,mask_set):
    """put all possible 0/1 mask code of ls[index:max_index] to mask_set"""
    if index > max_index:
        mask_set.append(list(working_set))
    else:
        working_set[index] = 0
        subset_mask(index + 1, max_index, working_set, mask_set)
        working_set[index] = 1
        subset_mask(index + 1, max_index, working_set, mask_set)

def subset(ls):
    """generate subsets according to mask codes"""
    result = []
    mask_set = []
    working_set = [0] * len(ls)
    subset_mask(0,len(working_set) - 1,working_set,mask_set)
    for mask in mask_set:
        result.append([j for i,j in zip(mask,ls) if i])
    return result

if __name__ == "__main__":
    ls = [1, 2 ,3]
    result = subset(ls)
    for r in result:
        print(r)
结果:
	[]
	[3]
	[2]
	[2, 3]
	[1]
	[1, 3]
	[1, 2]
	[1, 2, 3]

非递归版本

生成掩码的过程也可以借用二进制的形式,比如全集111对应2^3-1=7,000对应0,所以从0到2^n-1转为二进制形式即可生成所有掩码,代码如下:

def subset_mask(length):
    """return all possible 0/1 with length """
    mask_set = []
    num = 0 
    while num <= 2 ** length - 1: # max mask code in decimal mode
        mask_set.append(str(bin(num))[2:].zfill(length))
        num += 1
    return mask_set

def subset(ls):
    """generate subsets according to mask codes"""
    result = []
    mask_set = subset_mask(len(ls))
    for mask in mask_set:
        result.append([j for i,j in zip(mask,ls) if i == '1'])
    return result

if __name__ == "__main__":
    ls = [1, 2, 3]
    result = subset(ls)
    for r in result:
        print(r)
结果:
	[]
	[3]
	[2]
	[2, 3]
	[1]
	[1, 3]
	[1, 2]
	[1, 2, 3]

以上是关于算法-求集合所有子集的主要内容,如果未能解决你的问题,请参考以下文章

获取数组的子集数组

获取数组的子集数组

用回溯法解定和子集问题、0/1背包问题和n皇后问题的算法比较

HDU 5650

力扣算法题—078集合

求集合的所有子集问题