understanding better a solution for finding permutations of a string - javascript












3















I'm trying to get a better understanding of recursion as well as functional programming, I thought a good practice example for that would be to create permutations of a string with recursion and modern methods like reduce, filter and map.



I found this beautiful piece of code






const flatten = xs =>
xs.reduce((cum, next) => [...cum, ...next], );

const without = (xs, x) =>
xs.filter(y => y !== x);

const permutations = xs =>
flatten(xs.map(x =>
xs.length < 2
? [xs]
: permutations(without(xs, x)).map(perm => [x, ...perm])
));

permutations([1,2,3])
// [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]





from Permutations in JavaScript?
by Márton Sári



I've delimited it a bit in order to add some console logs to debug it and understand what's it doing behind the scenes






const flatten = xs => {
console.log(`input for flatten(${xs})`);
return xs.reduce((cum, next) => {
let res = [...cum, ...next];
console.log(`output from flatten(): ${res}`);
return res;
}, );
}

const without = (xs, x) => {
console.log(`input for without(${xs},${x})`)
let res = xs.filter(y => y !== x);
console.log(`output from without: ${res}`);
return res;
}

const permutations = xs => {
console.log(`input for permutations(${xs})`);
let res = flatten(xs.map(x => {
if (xs.length < 2) {
return [xs]
} else {
return permutations(without(xs, x)).map(perm => [x, ...perm])
}
}));
console.log(`output for permutations: ${res}`)
return res;
}

permutations([1,2,3])





I think I have a good enough idea of what each method iss doing, but I just can't seem to conceptualize how it all comes together to create [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]



can somebody show me step by step what's going on under the hood?










share|improve this question























  • the permutations are created in a step-by-step manner and recursively, first it checks to see if only one element is present since then there isn no permutation except it self. Then recursively apply a simple step, try each item and permute with the rest

    – Nikos M.
    Nov 14 '18 at 18:00
















3















I'm trying to get a better understanding of recursion as well as functional programming, I thought a good practice example for that would be to create permutations of a string with recursion and modern methods like reduce, filter and map.



I found this beautiful piece of code






const flatten = xs =>
xs.reduce((cum, next) => [...cum, ...next], );

const without = (xs, x) =>
xs.filter(y => y !== x);

const permutations = xs =>
flatten(xs.map(x =>
xs.length < 2
? [xs]
: permutations(without(xs, x)).map(perm => [x, ...perm])
));

permutations([1,2,3])
// [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]





from Permutations in JavaScript?
by Márton Sári



I've delimited it a bit in order to add some console logs to debug it and understand what's it doing behind the scenes






const flatten = xs => {
console.log(`input for flatten(${xs})`);
return xs.reduce((cum, next) => {
let res = [...cum, ...next];
console.log(`output from flatten(): ${res}`);
return res;
}, );
}

const without = (xs, x) => {
console.log(`input for without(${xs},${x})`)
let res = xs.filter(y => y !== x);
console.log(`output from without: ${res}`);
return res;
}

const permutations = xs => {
console.log(`input for permutations(${xs})`);
let res = flatten(xs.map(x => {
if (xs.length < 2) {
return [xs]
} else {
return permutations(without(xs, x)).map(perm => [x, ...perm])
}
}));
console.log(`output for permutations: ${res}`)
return res;
}

permutations([1,2,3])





I think I have a good enough idea of what each method iss doing, but I just can't seem to conceptualize how it all comes together to create [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]



can somebody show me step by step what's going on under the hood?










share|improve this question























  • the permutations are created in a step-by-step manner and recursively, first it checks to see if only one element is present since then there isn no permutation except it self. Then recursively apply a simple step, try each item and permute with the rest

    – Nikos M.
    Nov 14 '18 at 18:00














3












3








3


3






I'm trying to get a better understanding of recursion as well as functional programming, I thought a good practice example for that would be to create permutations of a string with recursion and modern methods like reduce, filter and map.



I found this beautiful piece of code






const flatten = xs =>
xs.reduce((cum, next) => [...cum, ...next], );

const without = (xs, x) =>
xs.filter(y => y !== x);

const permutations = xs =>
flatten(xs.map(x =>
xs.length < 2
? [xs]
: permutations(without(xs, x)).map(perm => [x, ...perm])
));

permutations([1,2,3])
// [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]





from Permutations in JavaScript?
by Márton Sári



I've delimited it a bit in order to add some console logs to debug it and understand what's it doing behind the scenes






const flatten = xs => {
console.log(`input for flatten(${xs})`);
return xs.reduce((cum, next) => {
let res = [...cum, ...next];
console.log(`output from flatten(): ${res}`);
return res;
}, );
}

const without = (xs, x) => {
console.log(`input for without(${xs},${x})`)
let res = xs.filter(y => y !== x);
console.log(`output from without: ${res}`);
return res;
}

const permutations = xs => {
console.log(`input for permutations(${xs})`);
let res = flatten(xs.map(x => {
if (xs.length < 2) {
return [xs]
} else {
return permutations(without(xs, x)).map(perm => [x, ...perm])
}
}));
console.log(`output for permutations: ${res}`)
return res;
}

permutations([1,2,3])





I think I have a good enough idea of what each method iss doing, but I just can't seem to conceptualize how it all comes together to create [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]



can somebody show me step by step what's going on under the hood?










share|improve this question














I'm trying to get a better understanding of recursion as well as functional programming, I thought a good practice example for that would be to create permutations of a string with recursion and modern methods like reduce, filter and map.



I found this beautiful piece of code






const flatten = xs =>
xs.reduce((cum, next) => [...cum, ...next], );

const without = (xs, x) =>
xs.filter(y => y !== x);

const permutations = xs =>
flatten(xs.map(x =>
xs.length < 2
? [xs]
: permutations(without(xs, x)).map(perm => [x, ...perm])
));

permutations([1,2,3])
// [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]





from Permutations in JavaScript?
by Márton Sári



I've delimited it a bit in order to add some console logs to debug it and understand what's it doing behind the scenes






const flatten = xs => {
console.log(`input for flatten(${xs})`);
return xs.reduce((cum, next) => {
let res = [...cum, ...next];
console.log(`output from flatten(): ${res}`);
return res;
}, );
}

const without = (xs, x) => {
console.log(`input for without(${xs},${x})`)
let res = xs.filter(y => y !== x);
console.log(`output from without: ${res}`);
return res;
}

const permutations = xs => {
console.log(`input for permutations(${xs})`);
let res = flatten(xs.map(x => {
if (xs.length < 2) {
return [xs]
} else {
return permutations(without(xs, x)).map(perm => [x, ...perm])
}
}));
console.log(`output for permutations: ${res}`)
return res;
}

permutations([1,2,3])





I think I have a good enough idea of what each method iss doing, but I just can't seem to conceptualize how it all comes together to create [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]



can somebody show me step by step what's going on under the hood?






const flatten = xs =>
xs.reduce((cum, next) => [...cum, ...next], );

const without = (xs, x) =>
xs.filter(y => y !== x);

const permutations = xs =>
flatten(xs.map(x =>
xs.length < 2
? [xs]
: permutations(without(xs, x)).map(perm => [x, ...perm])
));

permutations([1,2,3])
// [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]





const flatten = xs =>
xs.reduce((cum, next) => [...cum, ...next], );

const without = (xs, x) =>
xs.filter(y => y !== x);

const permutations = xs =>
flatten(xs.map(x =>
xs.length < 2
? [xs]
: permutations(without(xs, x)).map(perm => [x, ...perm])
));

permutations([1,2,3])
// [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]





const flatten = xs => {
console.log(`input for flatten(${xs})`);
return xs.reduce((cum, next) => {
let res = [...cum, ...next];
console.log(`output from flatten(): ${res}`);
return res;
}, );
}

const without = (xs, x) => {
console.log(`input for without(${xs},${x})`)
let res = xs.filter(y => y !== x);
console.log(`output from without: ${res}`);
return res;
}

const permutations = xs => {
console.log(`input for permutations(${xs})`);
let res = flatten(xs.map(x => {
if (xs.length < 2) {
return [xs]
} else {
return permutations(without(xs, x)).map(perm => [x, ...perm])
}
}));
console.log(`output for permutations: ${res}`)
return res;
}

permutations([1,2,3])





const flatten = xs => {
console.log(`input for flatten(${xs})`);
return xs.reduce((cum, next) => {
let res = [...cum, ...next];
console.log(`output from flatten(): ${res}`);
return res;
}, );
}

const without = (xs, x) => {
console.log(`input for without(${xs},${x})`)
let res = xs.filter(y => y !== x);
console.log(`output from without: ${res}`);
return res;
}

const permutations = xs => {
console.log(`input for permutations(${xs})`);
let res = flatten(xs.map(x => {
if (xs.length < 2) {
return [xs]
} else {
return permutations(without(xs, x)).map(perm => [x, ...perm])
}
}));
console.log(`output for permutations: ${res}`)
return res;
}

permutations([1,2,3])






javascript






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 14 '18 at 17:56









totalnoobtotalnoob

4191933




4191933













  • the permutations are created in a step-by-step manner and recursively, first it checks to see if only one element is present since then there isn no permutation except it self. Then recursively apply a simple step, try each item and permute with the rest

    – Nikos M.
    Nov 14 '18 at 18:00



















  • the permutations are created in a step-by-step manner and recursively, first it checks to see if only one element is present since then there isn no permutation except it self. Then recursively apply a simple step, try each item and permute with the rest

    – Nikos M.
    Nov 14 '18 at 18:00

















the permutations are created in a step-by-step manner and recursively, first it checks to see if only one element is present since then there isn no permutation except it self. Then recursively apply a simple step, try each item and permute with the rest

– Nikos M.
Nov 14 '18 at 18:00





the permutations are created in a step-by-step manner and recursively, first it checks to see if only one element is present since then there isn no permutation except it self. Then recursively apply a simple step, try each item and permute with the rest

– Nikos M.
Nov 14 '18 at 18:00












2 Answers
2






active

oldest

votes


















2














To get all permuations we do the following:



We take one element of the array from left to right.



 xs.map(x => // 1


For all the other elements we generate permutations recursively:



 permutations(without(xs, x)) // [[2, 3], [3, 2]]


for every permutation we add the value we've taken out back at the beginning:



 .map(perm => [xs, ...perm]) // [[1, 2, 3], [1, 3, 2]]


now that is repeated for all the arrays elements and it results in:



 [
// 1
[[1, 2, 3], [1, 3, 2]],
// 2
[[2, 1, 3], [2, 3, 1]],
// 3
[[3, 1, 2], [3, 2, 1]]
]


now we just have to flatten(...) that array to get the desired result.



The whole thing could be expressed as a tree of recursive calls:



 [1, 2, 3]
- [2, 3] ->
- [3] -> [1, 2, 3]
- [2] -> [1, 3, 2]
- [1, 3] ->
- [1] -> [2, 3, 1]
- [3] -> [2, 1, 3]
- [1, 2] ->
- [1] -> [3, 2, 1]
- [2] -> [3, 1, 2]





share|improve this answer
























  • how is the recursive magic generating the permutations inside of permutations()? once it gets the other elements from without()

    – totalnoob
    Nov 15 '18 at 15:30



















1















I've delimited it a bit in order to add some console logs to debug it




This can help of course. However keep in mind that simple recursive definitions can often result in complex execution traces.



That is in fact one of reasons why recursion can be so useful. Because some algorithms that have complicated iterations, admit a simple recursive description. So your goal in understanding a recursive algorithm should be to figure out the inductive (not iterative) reasoning in its definition.



Lets forget about javascript and focus on the algorithm. Let's see we can obtain the permutations of elements of a set A, which we will denote P(A).



Note: It's of no relevance that in the original algorithm the input is a list, since the original order does not matter at all. Likewise it's of no relevance that we will return a set of lists rather than a list of lists, since we don't care the order in which solutions are calculated.



Base Case:



The simplest case is the empty set. There is exactly one solution for the permutations of 0 elements, and that solution is the empty sequence . So,



P(A) = {}


Recursive Case:



In order to use recursion, you want to describe how to obtain P(A) from P(A') for some A' smaller than A in size.



Note: If you do that, it's finished. Operationally the program will work out via successive calls to P with smaller and smaller arguments until it reaches the base case, and then it will come back bulding longer results from shorter ones.



So here is one way to write a particular permutation of an A with n+1 elems. You need to successively pick one element of A for each position:



 _   _ ... _ 
n+1 n 1


So you pick an x ∈ A for the first



 x   _ ... _ 
n 1


And then you need to choose a permutation in P(A{x}).



This tells you one way to build all permutations of size n. Consider all possible choices of x in A (to use as first element), and for each choice put x in front of each solution of P(A{x}). Finally take the union of all solutions you found for each choice of x.



Let's use the dot operator to represent putting x in front of a sequence s, and the diamond operator to represent putting x in front of every s ∈ S. That is,



x⋅s = [x, s1, s2, ..., sn] 
x⟡S = {x⋅s : s ∈ S}


Then for a non-empty A



P(A) = ⋃ {x⟡P(A{x}) : x ∈ A} 


This expression together with the case base give you all the permutations of elements in a set A.



The javascript code



To understand how the code you've shown implements this algortithm you need to consider the following




  • That code considers two base cases, when you have 0 or 1 elements, by writing xs.length < 2. We could have done that too, it's irrelevant. You can change that 2 into a 1 and it should still work.


  • The mapping corresponds to our operation x⟡S = {x⋅s : s ∈ S}


  • The without corresponds to P(A{x})


  • The flatten corresponds to the which joins all solutions.







share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53306200%2funderstanding-better-a-solution-for-finding-permutations-of-a-string-javascrip%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    To get all permuations we do the following:



    We take one element of the array from left to right.



     xs.map(x => // 1


    For all the other elements we generate permutations recursively:



     permutations(without(xs, x)) // [[2, 3], [3, 2]]


    for every permutation we add the value we've taken out back at the beginning:



     .map(perm => [xs, ...perm]) // [[1, 2, 3], [1, 3, 2]]


    now that is repeated for all the arrays elements and it results in:



     [
    // 1
    [[1, 2, 3], [1, 3, 2]],
    // 2
    [[2, 1, 3], [2, 3, 1]],
    // 3
    [[3, 1, 2], [3, 2, 1]]
    ]


    now we just have to flatten(...) that array to get the desired result.



    The whole thing could be expressed as a tree of recursive calls:



     [1, 2, 3]
    - [2, 3] ->
    - [3] -> [1, 2, 3]
    - [2] -> [1, 3, 2]
    - [1, 3] ->
    - [1] -> [2, 3, 1]
    - [3] -> [2, 1, 3]
    - [1, 2] ->
    - [1] -> [3, 2, 1]
    - [2] -> [3, 1, 2]





    share|improve this answer
























    • how is the recursive magic generating the permutations inside of permutations()? once it gets the other elements from without()

      – totalnoob
      Nov 15 '18 at 15:30
















    2














    To get all permuations we do the following:



    We take one element of the array from left to right.



     xs.map(x => // 1


    For all the other elements we generate permutations recursively:



     permutations(without(xs, x)) // [[2, 3], [3, 2]]


    for every permutation we add the value we've taken out back at the beginning:



     .map(perm => [xs, ...perm]) // [[1, 2, 3], [1, 3, 2]]


    now that is repeated for all the arrays elements and it results in:



     [
    // 1
    [[1, 2, 3], [1, 3, 2]],
    // 2
    [[2, 1, 3], [2, 3, 1]],
    // 3
    [[3, 1, 2], [3, 2, 1]]
    ]


    now we just have to flatten(...) that array to get the desired result.



    The whole thing could be expressed as a tree of recursive calls:



     [1, 2, 3]
    - [2, 3] ->
    - [3] -> [1, 2, 3]
    - [2] -> [1, 3, 2]
    - [1, 3] ->
    - [1] -> [2, 3, 1]
    - [3] -> [2, 1, 3]
    - [1, 2] ->
    - [1] -> [3, 2, 1]
    - [2] -> [3, 1, 2]





    share|improve this answer
























    • how is the recursive magic generating the permutations inside of permutations()? once it gets the other elements from without()

      – totalnoob
      Nov 15 '18 at 15:30














    2












    2








    2







    To get all permuations we do the following:



    We take one element of the array from left to right.



     xs.map(x => // 1


    For all the other elements we generate permutations recursively:



     permutations(without(xs, x)) // [[2, 3], [3, 2]]


    for every permutation we add the value we've taken out back at the beginning:



     .map(perm => [xs, ...perm]) // [[1, 2, 3], [1, 3, 2]]


    now that is repeated for all the arrays elements and it results in:



     [
    // 1
    [[1, 2, 3], [1, 3, 2]],
    // 2
    [[2, 1, 3], [2, 3, 1]],
    // 3
    [[3, 1, 2], [3, 2, 1]]
    ]


    now we just have to flatten(...) that array to get the desired result.



    The whole thing could be expressed as a tree of recursive calls:



     [1, 2, 3]
    - [2, 3] ->
    - [3] -> [1, 2, 3]
    - [2] -> [1, 3, 2]
    - [1, 3] ->
    - [1] -> [2, 3, 1]
    - [3] -> [2, 1, 3]
    - [1, 2] ->
    - [1] -> [3, 2, 1]
    - [2] -> [3, 1, 2]





    share|improve this answer













    To get all permuations we do the following:



    We take one element of the array from left to right.



     xs.map(x => // 1


    For all the other elements we generate permutations recursively:



     permutations(without(xs, x)) // [[2, 3], [3, 2]]


    for every permutation we add the value we've taken out back at the beginning:



     .map(perm => [xs, ...perm]) // [[1, 2, 3], [1, 3, 2]]


    now that is repeated for all the arrays elements and it results in:



     [
    // 1
    [[1, 2, 3], [1, 3, 2]],
    // 2
    [[2, 1, 3], [2, 3, 1]],
    // 3
    [[3, 1, 2], [3, 2, 1]]
    ]


    now we just have to flatten(...) that array to get the desired result.



    The whole thing could be expressed as a tree of recursive calls:



     [1, 2, 3]
    - [2, 3] ->
    - [3] -> [1, 2, 3]
    - [2] -> [1, 3, 2]
    - [1, 3] ->
    - [1] -> [2, 3, 1]
    - [3] -> [2, 1, 3]
    - [1, 2] ->
    - [1] -> [3, 2, 1]
    - [2] -> [3, 1, 2]






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 14 '18 at 18:04









    Jonas WilmsJonas Wilms

    58.6k43151




    58.6k43151













    • how is the recursive magic generating the permutations inside of permutations()? once it gets the other elements from without()

      – totalnoob
      Nov 15 '18 at 15:30



















    • how is the recursive magic generating the permutations inside of permutations()? once it gets the other elements from without()

      – totalnoob
      Nov 15 '18 at 15:30

















    how is the recursive magic generating the permutations inside of permutations()? once it gets the other elements from without()

    – totalnoob
    Nov 15 '18 at 15:30





    how is the recursive magic generating the permutations inside of permutations()? once it gets the other elements from without()

    – totalnoob
    Nov 15 '18 at 15:30













    1















    I've delimited it a bit in order to add some console logs to debug it




    This can help of course. However keep in mind that simple recursive definitions can often result in complex execution traces.



    That is in fact one of reasons why recursion can be so useful. Because some algorithms that have complicated iterations, admit a simple recursive description. So your goal in understanding a recursive algorithm should be to figure out the inductive (not iterative) reasoning in its definition.



    Lets forget about javascript and focus on the algorithm. Let's see we can obtain the permutations of elements of a set A, which we will denote P(A).



    Note: It's of no relevance that in the original algorithm the input is a list, since the original order does not matter at all. Likewise it's of no relevance that we will return a set of lists rather than a list of lists, since we don't care the order in which solutions are calculated.



    Base Case:



    The simplest case is the empty set. There is exactly one solution for the permutations of 0 elements, and that solution is the empty sequence . So,



    P(A) = {}


    Recursive Case:



    In order to use recursion, you want to describe how to obtain P(A) from P(A') for some A' smaller than A in size.



    Note: If you do that, it's finished. Operationally the program will work out via successive calls to P with smaller and smaller arguments until it reaches the base case, and then it will come back bulding longer results from shorter ones.



    So here is one way to write a particular permutation of an A with n+1 elems. You need to successively pick one element of A for each position:



     _   _ ... _ 
    n+1 n 1


    So you pick an x ∈ A for the first



     x   _ ... _ 
    n 1


    And then you need to choose a permutation in P(A{x}).



    This tells you one way to build all permutations of size n. Consider all possible choices of x in A (to use as first element), and for each choice put x in front of each solution of P(A{x}). Finally take the union of all solutions you found for each choice of x.



    Let's use the dot operator to represent putting x in front of a sequence s, and the diamond operator to represent putting x in front of every s ∈ S. That is,



    x⋅s = [x, s1, s2, ..., sn] 
    x⟡S = {x⋅s : s ∈ S}


    Then for a non-empty A



    P(A) = ⋃ {x⟡P(A{x}) : x ∈ A} 


    This expression together with the case base give you all the permutations of elements in a set A.



    The javascript code



    To understand how the code you've shown implements this algortithm you need to consider the following




    • That code considers two base cases, when you have 0 or 1 elements, by writing xs.length < 2. We could have done that too, it's irrelevant. You can change that 2 into a 1 and it should still work.


    • The mapping corresponds to our operation x⟡S = {x⋅s : s ∈ S}


    • The without corresponds to P(A{x})


    • The flatten corresponds to the which joins all solutions.







    share|improve this answer






























      1















      I've delimited it a bit in order to add some console logs to debug it




      This can help of course. However keep in mind that simple recursive definitions can often result in complex execution traces.



      That is in fact one of reasons why recursion can be so useful. Because some algorithms that have complicated iterations, admit a simple recursive description. So your goal in understanding a recursive algorithm should be to figure out the inductive (not iterative) reasoning in its definition.



      Lets forget about javascript and focus on the algorithm. Let's see we can obtain the permutations of elements of a set A, which we will denote P(A).



      Note: It's of no relevance that in the original algorithm the input is a list, since the original order does not matter at all. Likewise it's of no relevance that we will return a set of lists rather than a list of lists, since we don't care the order in which solutions are calculated.



      Base Case:



      The simplest case is the empty set. There is exactly one solution for the permutations of 0 elements, and that solution is the empty sequence . So,



      P(A) = {}


      Recursive Case:



      In order to use recursion, you want to describe how to obtain P(A) from P(A') for some A' smaller than A in size.



      Note: If you do that, it's finished. Operationally the program will work out via successive calls to P with smaller and smaller arguments until it reaches the base case, and then it will come back bulding longer results from shorter ones.



      So here is one way to write a particular permutation of an A with n+1 elems. You need to successively pick one element of A for each position:



       _   _ ... _ 
      n+1 n 1


      So you pick an x ∈ A for the first



       x   _ ... _ 
      n 1


      And then you need to choose a permutation in P(A{x}).



      This tells you one way to build all permutations of size n. Consider all possible choices of x in A (to use as first element), and for each choice put x in front of each solution of P(A{x}). Finally take the union of all solutions you found for each choice of x.



      Let's use the dot operator to represent putting x in front of a sequence s, and the diamond operator to represent putting x in front of every s ∈ S. That is,



      x⋅s = [x, s1, s2, ..., sn] 
      x⟡S = {x⋅s : s ∈ S}


      Then for a non-empty A



      P(A) = ⋃ {x⟡P(A{x}) : x ∈ A} 


      This expression together with the case base give you all the permutations of elements in a set A.



      The javascript code



      To understand how the code you've shown implements this algortithm you need to consider the following




      • That code considers two base cases, when you have 0 or 1 elements, by writing xs.length < 2. We could have done that too, it's irrelevant. You can change that 2 into a 1 and it should still work.


      • The mapping corresponds to our operation x⟡S = {x⋅s : s ∈ S}


      • The without corresponds to P(A{x})


      • The flatten corresponds to the which joins all solutions.







      share|improve this answer




























        1












        1








        1








        I've delimited it a bit in order to add some console logs to debug it




        This can help of course. However keep in mind that simple recursive definitions can often result in complex execution traces.



        That is in fact one of reasons why recursion can be so useful. Because some algorithms that have complicated iterations, admit a simple recursive description. So your goal in understanding a recursive algorithm should be to figure out the inductive (not iterative) reasoning in its definition.



        Lets forget about javascript and focus on the algorithm. Let's see we can obtain the permutations of elements of a set A, which we will denote P(A).



        Note: It's of no relevance that in the original algorithm the input is a list, since the original order does not matter at all. Likewise it's of no relevance that we will return a set of lists rather than a list of lists, since we don't care the order in which solutions are calculated.



        Base Case:



        The simplest case is the empty set. There is exactly one solution for the permutations of 0 elements, and that solution is the empty sequence . So,



        P(A) = {}


        Recursive Case:



        In order to use recursion, you want to describe how to obtain P(A) from P(A') for some A' smaller than A in size.



        Note: If you do that, it's finished. Operationally the program will work out via successive calls to P with smaller and smaller arguments until it reaches the base case, and then it will come back bulding longer results from shorter ones.



        So here is one way to write a particular permutation of an A with n+1 elems. You need to successively pick one element of A for each position:



         _   _ ... _ 
        n+1 n 1


        So you pick an x ∈ A for the first



         x   _ ... _ 
        n 1


        And then you need to choose a permutation in P(A{x}).



        This tells you one way to build all permutations of size n. Consider all possible choices of x in A (to use as first element), and for each choice put x in front of each solution of P(A{x}). Finally take the union of all solutions you found for each choice of x.



        Let's use the dot operator to represent putting x in front of a sequence s, and the diamond operator to represent putting x in front of every s ∈ S. That is,



        x⋅s = [x, s1, s2, ..., sn] 
        x⟡S = {x⋅s : s ∈ S}


        Then for a non-empty A



        P(A) = ⋃ {x⟡P(A{x}) : x ∈ A} 


        This expression together with the case base give you all the permutations of elements in a set A.



        The javascript code



        To understand how the code you've shown implements this algortithm you need to consider the following




        • That code considers two base cases, when you have 0 or 1 elements, by writing xs.length < 2. We could have done that too, it's irrelevant. You can change that 2 into a 1 and it should still work.


        • The mapping corresponds to our operation x⟡S = {x⋅s : s ∈ S}


        • The without corresponds to P(A{x})


        • The flatten corresponds to the which joins all solutions.







        share|improve this answer
















        I've delimited it a bit in order to add some console logs to debug it




        This can help of course. However keep in mind that simple recursive definitions can often result in complex execution traces.



        That is in fact one of reasons why recursion can be so useful. Because some algorithms that have complicated iterations, admit a simple recursive description. So your goal in understanding a recursive algorithm should be to figure out the inductive (not iterative) reasoning in its definition.



        Lets forget about javascript and focus on the algorithm. Let's see we can obtain the permutations of elements of a set A, which we will denote P(A).



        Note: It's of no relevance that in the original algorithm the input is a list, since the original order does not matter at all. Likewise it's of no relevance that we will return a set of lists rather than a list of lists, since we don't care the order in which solutions are calculated.



        Base Case:



        The simplest case is the empty set. There is exactly one solution for the permutations of 0 elements, and that solution is the empty sequence . So,



        P(A) = {}


        Recursive Case:



        In order to use recursion, you want to describe how to obtain P(A) from P(A') for some A' smaller than A in size.



        Note: If you do that, it's finished. Operationally the program will work out via successive calls to P with smaller and smaller arguments until it reaches the base case, and then it will come back bulding longer results from shorter ones.



        So here is one way to write a particular permutation of an A with n+1 elems. You need to successively pick one element of A for each position:



         _   _ ... _ 
        n+1 n 1


        So you pick an x ∈ A for the first



         x   _ ... _ 
        n 1


        And then you need to choose a permutation in P(A{x}).



        This tells you one way to build all permutations of size n. Consider all possible choices of x in A (to use as first element), and for each choice put x in front of each solution of P(A{x}). Finally take the union of all solutions you found for each choice of x.



        Let's use the dot operator to represent putting x in front of a sequence s, and the diamond operator to represent putting x in front of every s ∈ S. That is,



        x⋅s = [x, s1, s2, ..., sn] 
        x⟡S = {x⋅s : s ∈ S}


        Then for a non-empty A



        P(A) = ⋃ {x⟡P(A{x}) : x ∈ A} 


        This expression together with the case base give you all the permutations of elements in a set A.



        The javascript code



        To understand how the code you've shown implements this algortithm you need to consider the following




        • That code considers two base cases, when you have 0 or 1 elements, by writing xs.length < 2. We could have done that too, it's irrelevant. You can change that 2 into a 1 and it should still work.


        • The mapping corresponds to our operation x⟡S = {x⋅s : s ∈ S}


        • The without corresponds to P(A{x})


        • The flatten corresponds to the which joins all solutions.








        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 16 '18 at 2:16

























        answered Nov 15 '18 at 17:34









        Jorge AdrianoJorge Adriano

        2,220918




        2,220918






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53306200%2funderstanding-better-a-solution-for-finding-permutations-of-a-string-javascrip%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            The Sandy Post

            Danny Elfman

            Pages that link to "Head v. Amoskeag Manufacturing Co."