I need a subroutine that, given a set of characters, will generate all possible combinations of those characters of length k. Order matters and reuse is allowed, so if k = 2
then AB != BA
and AA
is an option. I found some working examples on PerlMonks, but unfortunately they are code golf and not easy for me to wrap my mind around. Can someone please do one or more of the following?
- Give a breakdown and explanation of how the first algorithm works.
- De-obfuscate the code so that the meaning is clearer.
- Point me toward another example that is clearer.
Thanks!
I had a look at the very first piece of code on the page you referred to:
I have spread it out a bit to make it more readable; also I have made some changes to it to make it clearer (see
combinations
):Both versions work in the same way, and they produce exactly the same output:
I included some comments in the code, but perhaps a lengthier explanation is in order!? Well, here's an example to illustrate how things work: let's say we've got two items, "A" and "B", and we want to get all possible combinations of 2 of these items. In that case,
$number
will initially be equal to 2 (as we want to get pairs), and@chars
will be equal to('A', 'B')
.The first time
combinations
is called,$number
is decremented to 1, thus theif
condition is met, and we enter theforeach
loop. This first sets$char
to 'A'. It then callscombinations(1, ('A', 'B'))
. As$number
always gets decremented when the subroutine is called,$number
is 0 in this 'child subroutine', consequently the child simply returns ('A', 'B'). Thus:map
then takes both 'A' and 'B' and concatenates each with 'A' ($char), thus@intermediate_list
is ('AA', 'AB'). In the next round of theforeach
loop, the same is done with$char = B
, which sets@intermediate_list
to ('BA', 'BB').In each round the contents of
@intermediate_list
are pushed into the result list, hence@result
eventually contains all possible combinations.If you want to get triplets instead of pairs, you will obviously start with
$number = 3
, andcombinations
will be called three times. The second time it's called it will return@result
, i.e. a pair-containing list. Each item from that list will be concatenated with each character of the initial character set.Okay, I hope this makes sense. Please ask in case something has not become clear.
EDIT: Please see ysth's comment below.
You can use variations_with_repetition from Algorithm::Combinatorics (which also provides an iterator-based interface), but if you just need a list, this is a fairly simple recursive algorithm:
This is basically the same algorithm the code golfers are using, but I'm using a
for
loop instead of nestingmap
. Also, I recurse only once per level (code golf is about minimizing source code, not runtime).Any recursive function can be converted to an iterative one, which usually reduces its overhead. This one is fairly simple:
This version handles the
$k == 0
case, which the original did not.