2

I just can't understand how this algorithm works. All the explanations I've seen say that if you have a set such as {A, B, C} and you want all the permutations, start with each letter distinctly, then find the permutations of the rest of the letters. So for example {A} + permutationsOf({B,C}).

But all the explanations seem to gloss over how you find the permutations of the rest. An example being this one.

Could someone try to explain this algorithm a little more clearly to me?

Community
  • 1
  • 1
Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • This question made my day. But seriously, it's quite reasonable, I'm looking forward for someone with truly exceptional teaching skills to answer. :) – siledh Oct 07 '13 at 13:53

4 Answers4

1

To understand recursion you need to understand recursion..

(c) Programmer's wisdom

Your question is about that fact, that "permutations of the rest" is that recursive part. Recursion always consist of two parts: trivial case and recursion case. Trivial case points to a case when there's no continue for recursion and something should be returned.

In your sample, trivial part would be {A} - there's only one permutation of this set - itself. Recursion part will be union of current element and this "rest part" - i.e. if you have more than one element, then your result will be union of permutation between this element and "rest part". In terms of permutation: the rest part is current set without selected element. I.e. for set {A,B,C} on first recursion step that will be {A} and "rest part": {B,C}, then {B} and "rest part": {A,C} - and, finally, {C} with "rest part": {A,B}

So your recursion will last till the moment when "the rest part" will be single element - and then it will end.

Alma Do
  • 37,009
  • 9
  • 76
  • 105
  • I get that we keep breaking it down into further problems that can be solved similarly, but how does it get broken down? Is it like divide and conquers where it's split and half? I guess when I see `permutationsOf(theRest)` I just don't know how that rest part is broken down. – Doug Smith Oct 07 '13 at 14:05
  • @DougSmith I've updated (it was pointed in my question before, but now I've made that explicit) – Alma Do Oct 07 '13 at 14:08
  • In terms of that example, in my original example I thought it was broken down as having A, B, and C be the starting points, and then the permutations of the rest of those were built? Is this the same? – Doug Smith Oct 07 '13 at 14:14
  • You're iterating through `{A, B, C}` set. On each step you have current element (iterator) - and then you're getting "rest part" as subtraction of this element from entire array. If this "rest part" is an array - then you're adding current element with permutations of it to your result - that's it. – Alma Do Oct 07 '13 at 14:17
0

That is the whole point of recursive implementation. You define the solution recursively assuming you already have the solution for the simpler problem. With a little tought you will come to the conclusion that you can do the very same consideration for the simpler case making it even more simple. Going on until you reach a case that is simple enough to solve. This simple enough case is known as bottom for the recursion.

Also please note that you have to iterate over all letters not just A being the first element. Thus you get all permutations as:

{{A} + permutationsOf({B,C})} +{{B} + permutationsOf({A,C})} + {{C} + permutationsOf({A,B})}

Take a minute and try to write down all the permutations of a set of four letters say {A, B, C, D}. You will find that the algorithm you use is close to the recursion above.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • Alright, say I have {A, B, C, D}, so I go A + P(BCD). Do I then go B + P(CD) then C + P(D)? How would that get me A B D C for example then? – Doug Smith Oct 07 '13 at 14:17
  • `P(BCD)` should return all permutations of `B,C and D` and thus just as I added in my post it should return `{{B} + P(CD)} + {{C} + P(BD)} + {{D} + P(BC)}`. You will see that the permutation you mention is included in the ones above. – Ivaylo Strandjev Oct 07 '13 at 14:19
0

So, let's analyze the example {A, B, C}.

First, you want to take single element out of it, and get the rest. So you would need to write some function that would return a list of pairs:

pairs = [ (A, {B, C})
          (B, {A, C})
          (C, {A, B}) ]

for each of these pairs, you get a separate list of permutations that can be made out of it, like that:

for pair in pairs do
    head  <- pair.fst // e.g. for the first pair it will be A
    tails <- perms(pair.snd) // e.g. tails will be a list of permutations computed from {B, C}

You need to attach the head to each tail from tails to get a complete permutation. So the complete loop will be:

permutations <- []
for pair in pairs do
    head  <- pair.fst // e.g. for the first pair it will be A
    tails <- perms(pair.snd) // e.g. tails will be a list of permutations computed from {B, C}

    for tail in tails do
        permutations.add(head :: tail); // here we create a complete permutation

head :: tail means that we attach one element head to the beginning of the list tail.

Well now, how to implement perms function used in the fragment tails <- perm(pair.snd). We just did! That's what recursion is all about. :)

We still need a base case, so:

perms({X}) = [ {X} ] // return a list of one possible permutation

And the function for all other cases looks like that:

perms({X...}) = 
  permutations <- []
  pairs <- createPairs({X...})
  for pair in pairs do
    head  <- pair.fst // e.g. for the first pair it will be A
    tails <- perms(pair.snd) // e.g. tails will be a list of permutations computed from {B, C}

    for tail in tails do
        permutations.add( head :: tail ); // here we create a complete permutation

 return permutations
siledh
  • 3,268
  • 2
  • 16
  • 29
0

The answer to your question is in the halting-criterion (in this case !inputString.length).

http://jsfiddle.net/mzPpa/

function permutate(inputString, outputString) {
    if (!inputString.length) console.log(outputString);
    else for (var i = 0; i < inputString.length; ++i) {
        permutate(inputString.substring(0, i) +
                  inputString.substring(i + 1),
                  outputString + inputString[i]);
    }
}

var inputString = "abcd";
var outputString = "";
permutate(inputString, outputString);
jgroenen
  • 1,332
  • 1
  • 8
  • 13