[Tutor] recursion and power sets
Tim Peters
tim.one at comcast.net
Sun Mar 7 20:53:23 EST 2004
[alice]
> Ouch! I think I've injured my brain on this one.
> I'm beginning to think recursion is not so cool after all.....
>
> I was trying to write a function that would take a set -
> represented as a list with non repeating elements - and return
> the power set, so given:
>
> [1,2,3]
>
> it should return:
>
> [[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
>
> After much general confusion, this is what I eventually came up with:
... [code to generate in order of size] ...
> This gives the subsets in a simmilar order to the one in which I
> would find them if I was doing it by hand, but its kind of difficult
> to think about, so I thought about it a bit more and came up with:
... [code that looks at the integers in range(2**len(set)), and
picks apart the bits to select subsets] ...
> Even though this algorithm gives the subsets in a completely
> different order to the way I'd normally find them by hand, I'm
> guessing its closer to the "canonical" algorithm for finding power
> sets than my recursive attempt, am I right?
There isn't really a canonical way -- "whatever works" is fine.
> or is there a third, completely different way of doing this?
Oh, many <wink>. There are 2**N pieces in the result, and the factorial of
that many ways to order them. Here's a particularly concsise way of
generating the powerset:
def powset(seq):
if seq:
head, tail = seq[:1], seq[1:]
for smaller in powset(tail):
yield smaller
yield head + smaller
else:
yield []
Given that, then
for s in powset([1, 2, 3]):
print s
prints
[]
[1]
[2]
[1, 2]
[3]
[1, 3]
[2, 3]
[1, 2, 3]
That's close to "canonical" in the sense that it closely follows a natural
way of *thinking* about the problem. Suppose we already had the powerset of
a set S, and wanted the powerset of the set T consisting of S and a new
element x. Well, each element of the powerset of T either does or doesn't
contain x.
If it doesn't contain x, it must be an element of the powerset of S too.
Conversely, each element of the powerset of S is also an element of the
powerset of T.
On the other hand, if it does contain x, then if we took x out of it we'd
also be left with an element of the powerset of S; and adding x to an
element of the powerset of S must conversely give an element of the powerset
of T.
Put it all together, and the powerset of T is the powerset of S, plus the
powerset of S fiddled by adding x to each element.
That's how the code above works:
if S isn't empty:
pick the first element and call it "head"
call the rest of S "tail"
for each element e of the powerset of tail:
one element of the powerset of S is e
another is e plus head
Now that's a naturally recursive way of thinking about the problem (we think
about how the powerset of a set is related to the powerset of a smaller
set), so it leads to simple recursive code. Your
> I'm beginning to think recursion is not so cool after all.....
comes from trying to force a recursive solution out of a non-recursive way
of thinking about the problem. That is indeed painful <wink>.
More information about the Tutor
mailing list