集合的所有子集
本文为 SICP 练习2.32 题解。
如何编写 Scheme 函数求集合的所有子集?
分析
例如,
S
=
{
1
,
2
,
3
}
S=\{1,2,3\}
S={1,2,3}。其所有子集:
{
}
,
{
1
}
,
{
2
}
,
{
3
}
,
{
1
,
2
}
,
{
1
,
3
}
,
{
2
,
3
}
,
{
1
,
2
,
3
}
\{\},\{1\},\{2\},\{3\},\{1,2\},\{1,3\},\{2,3\},\{1,2,3\}
{},{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}
再看
S
′
=
{
2
,
3
}
S'=\{2,3\}
S′={2,3}:
{
}
,
{
2
}
,
{
3
}
,
{
2
,
3
}
\{\},\{2\},\{3\},\{2,3\}
{},{2},{3},{2,3}
为了显出规律,将
S
S
S的子集分成两组,含1与不含1:
{
}
,
{
2
}
,
{
3
}
,
{
2
,
3
}
{
1
}
,
{
1
,
2
}
,
{
1
,
3
}
,
{
1
,
2
,
3
}
\{\},\{2\},\{3\},\{2,3\}\\ \{1\},\{1,2\},\{1,3\},\{1,2,3\}
{},{2},{3},{2,3}{1},{1,2},{1,3},{1,2,3}
递进地看这个问题,集合每增加一个元素,就多出一组包含该元素的子集,子集数量恰好翻倍。
我们又知道,
s
u
b
s
e
t
s
(
{
}
)
=
{
{
}
}
{\rm subsets}(\{\})=\{\{\}\}
subsets({})={{}}
可以写出递归思路了:
-
空集,返回{{}}
-
s u b s e t s ( S ) = s u b s e t s ( S ′ ) ∪ ( a → s u b s e t s ( S ′ ) ) {\rm subsets}(S)={\rm subsets}(S')\cup (a\rightarrow{\rm subsets}(S')) subsets(S)=subsets(S′)∪(a→subsets(S′))
其中, S = S ′ ⊕ { a } S=S'\oplus\{a\} S=S′⊕{a}, a → S a\rightarrow S a→S是指把 a a a加入 S S S的每一个元素中
实现
Scheme
需要一个帮助函数:
(define (extend list1 list2)
(if (null? list1)
list2
(cons (car list1) (extend (cdr list1) list2))))
主要函数:
(define (subsets s)
(define (f x) (cons (car s) x))
(if (null? s)
(list '())
(let ((rest (subsets (cdr s))))
(extend rest (map f rest)))))
python
def subsets(s):
if s == []:
return [[]]
else:
rest = subsets(s[:-1])
return rest + [x + [s[-1]] for x in rest]
两份代码几乎可以一一对应,不得不说,python 的表现力确实很强。