Algorithmic complexity of this algorithm to find all ordered permutations of length X

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
1
down vote

favorite












I have written a recursive function to generate the list of all ordered permutations of length X for a list of chars.



For instance: ['a', 'b', 'c', 'd'] with X=2 will give [['a', 'a'], ['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'a'], ['b', 'b'], ..., ['d', 'd']]



I'm not sure about its algorithmic complexity though (at least I know it's pretty horrible). I would say it's something around:



O(X * N^(L + X))



(where L is the number of different chars, 4 here because we have 'A', 'B', 'C', 'D', and X the length of the permutations we want to generate). Because I have 2 nested loops, which will be run X times (well, X-1 because of the special case when X = 1). Is it correct?



def generate_permutations(symbols, permutations_length):
if permutations_length == 1:
return [[symbol] for symbol in symbols]

tails = generate_permutations(symbols, permutations_length-1)
permutations =

for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)

return permutations

print(generate_permutations(['a', 'b', 'c', 'd'], 2))


By the way: I know this is not idiomatic Python and I apologize if it's ugly but it's just some prototyping I'm doing before writing this code in a different, less expressive language. And I also know that I could use itertools.permutations to do this task. By the way, I'd be interested if someone happens to know the algorithmic complexity of itertool's permutations function.







share|improve this question





















  • "And I also know that I could use itertools.permutations to do this task." So why didn't you?
    – Mast
    Apr 27 at 11:25










  • For the sake of the exercise and to learn how to do things by myself.
    – Matthieu
    Apr 27 at 11:53
















up vote
1
down vote

favorite












I have written a recursive function to generate the list of all ordered permutations of length X for a list of chars.



For instance: ['a', 'b', 'c', 'd'] with X=2 will give [['a', 'a'], ['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'a'], ['b', 'b'], ..., ['d', 'd']]



I'm not sure about its algorithmic complexity though (at least I know it's pretty horrible). I would say it's something around:



O(X * N^(L + X))



(where L is the number of different chars, 4 here because we have 'A', 'B', 'C', 'D', and X the length of the permutations we want to generate). Because I have 2 nested loops, which will be run X times (well, X-1 because of the special case when X = 1). Is it correct?



def generate_permutations(symbols, permutations_length):
if permutations_length == 1:
return [[symbol] for symbol in symbols]

tails = generate_permutations(symbols, permutations_length-1)
permutations =

for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)

return permutations

print(generate_permutations(['a', 'b', 'c', 'd'], 2))


By the way: I know this is not idiomatic Python and I apologize if it's ugly but it's just some prototyping I'm doing before writing this code in a different, less expressive language. And I also know that I could use itertools.permutations to do this task. By the way, I'd be interested if someone happens to know the algorithmic complexity of itertool's permutations function.







share|improve this question





















  • "And I also know that I could use itertools.permutations to do this task." So why didn't you?
    – Mast
    Apr 27 at 11:25










  • For the sake of the exercise and to learn how to do things by myself.
    – Matthieu
    Apr 27 at 11:53












up vote
1
down vote

favorite









up vote
1
down vote

favorite











I have written a recursive function to generate the list of all ordered permutations of length X for a list of chars.



For instance: ['a', 'b', 'c', 'd'] with X=2 will give [['a', 'a'], ['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'a'], ['b', 'b'], ..., ['d', 'd']]



I'm not sure about its algorithmic complexity though (at least I know it's pretty horrible). I would say it's something around:



O(X * N^(L + X))



(where L is the number of different chars, 4 here because we have 'A', 'B', 'C', 'D', and X the length of the permutations we want to generate). Because I have 2 nested loops, which will be run X times (well, X-1 because of the special case when X = 1). Is it correct?



def generate_permutations(symbols, permutations_length):
if permutations_length == 1:
return [[symbol] for symbol in symbols]

tails = generate_permutations(symbols, permutations_length-1)
permutations =

for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)

return permutations

print(generate_permutations(['a', 'b', 'c', 'd'], 2))


By the way: I know this is not idiomatic Python and I apologize if it's ugly but it's just some prototyping I'm doing before writing this code in a different, less expressive language. And I also know that I could use itertools.permutations to do this task. By the way, I'd be interested if someone happens to know the algorithmic complexity of itertool's permutations function.







share|improve this question













I have written a recursive function to generate the list of all ordered permutations of length X for a list of chars.



For instance: ['a', 'b', 'c', 'd'] with X=2 will give [['a', 'a'], ['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'a'], ['b', 'b'], ..., ['d', 'd']]



I'm not sure about its algorithmic complexity though (at least I know it's pretty horrible). I would say it's something around:



O(X * N^(L + X))



(where L is the number of different chars, 4 here because we have 'A', 'B', 'C', 'D', and X the length of the permutations we want to generate). Because I have 2 nested loops, which will be run X times (well, X-1 because of the special case when X = 1). Is it correct?



def generate_permutations(symbols, permutations_length):
if permutations_length == 1:
return [[symbol] for symbol in symbols]

tails = generate_permutations(symbols, permutations_length-1)
permutations =

for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)

return permutations

print(generate_permutations(['a', 'b', 'c', 'd'], 2))


By the way: I know this is not idiomatic Python and I apologize if it's ugly but it's just some prototyping I'm doing before writing this code in a different, less expressive language. And I also know that I could use itertools.permutations to do this task. By the way, I'd be interested if someone happens to know the algorithmic complexity of itertool's permutations function.









share|improve this question












share|improve this question




share|improve this question








edited Apr 27 at 12:16









Mast

7,32763484




7,32763484









asked Apr 27 at 10:13









Matthieu

112




112











  • "And I also know that I could use itertools.permutations to do this task." So why didn't you?
    – Mast
    Apr 27 at 11:25










  • For the sake of the exercise and to learn how to do things by myself.
    – Matthieu
    Apr 27 at 11:53
















  • "And I also know that I could use itertools.permutations to do this task." So why didn't you?
    – Mast
    Apr 27 at 11:25










  • For the sake of the exercise and to learn how to do things by myself.
    – Matthieu
    Apr 27 at 11:53















"And I also know that I could use itertools.permutations to do this task." So why didn't you?
– Mast
Apr 27 at 11:25




"And I also know that I could use itertools.permutations to do this task." So why didn't you?
– Mast
Apr 27 at 11:25












For the sake of the exercise and to learn how to do things by myself.
– Matthieu
Apr 27 at 11:53




For the sake of the exercise and to learn how to do things by myself.
– Matthieu
Apr 27 at 11:53










1 Answer
1






active

oldest

votes

















up vote
1
down vote



accepted











def generate_permutations(symbols, permutations_length):



These are not permutations. They are Cartesian products. In addition, calling the function generate_something suggests that what's returned might be a generator, which is probably what you want for a function which returns a stupendously large data structure, but is not what you get from this function.





 for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)



If you use extend instead of append then you give better hints as to how much underlying buffers might need to be increased:



 for tail in tails:
result.extend([symbol] + tail for symbol in symbols)



Performance: with an alphabet of size $L$ let $T_L(X)$ be the cost of calling this function for products of length $X$. Then $T_L(1) = L$ and $T_L(X+1) = T_L(X) + L L^X (X+2)$ assuming that constructing the list [symbol] + tail takes time proportional to its length and extending the list of results take amortised constant time per element added. This gives $Theta(L^XX)$ complexity.




The same complexity but much better memory efficiency can be achieved with a true generator approach which is based on the fact that all you really need to do is count to $L^X$ and convert into base $L$.






share|improve this answer





















  • Thanks a lot for this explanation! And I agree that generate_something was not the best way to name it. May I ask how you would do the same thing with a generator instead?
    – Matthieu
    Apr 27 at 11:55











  • @Matthieu: suppose that instead of 'a', 'b', 'c', 'd' your elements are 0, 1, 2, 3 and the Cartesian products are 00, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33. Does that make it clear what I mean by counting to 16 and converting into base 4?
    – Peter Taylor
    Apr 27 at 12:37










  • Yes, I get it! Thanks
    – Matthieu
    Apr 27 at 12:57










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

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: "196"
;
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',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193066%2falgorithmic-complexity-of-this-algorithm-to-find-all-ordered-permutations-of-len%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote



accepted











def generate_permutations(symbols, permutations_length):



These are not permutations. They are Cartesian products. In addition, calling the function generate_something suggests that what's returned might be a generator, which is probably what you want for a function which returns a stupendously large data structure, but is not what you get from this function.





 for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)



If you use extend instead of append then you give better hints as to how much underlying buffers might need to be increased:



 for tail in tails:
result.extend([symbol] + tail for symbol in symbols)



Performance: with an alphabet of size $L$ let $T_L(X)$ be the cost of calling this function for products of length $X$. Then $T_L(1) = L$ and $T_L(X+1) = T_L(X) + L L^X (X+2)$ assuming that constructing the list [symbol] + tail takes time proportional to its length and extending the list of results take amortised constant time per element added. This gives $Theta(L^XX)$ complexity.




The same complexity but much better memory efficiency can be achieved with a true generator approach which is based on the fact that all you really need to do is count to $L^X$ and convert into base $L$.






share|improve this answer





















  • Thanks a lot for this explanation! And I agree that generate_something was not the best way to name it. May I ask how you would do the same thing with a generator instead?
    – Matthieu
    Apr 27 at 11:55











  • @Matthieu: suppose that instead of 'a', 'b', 'c', 'd' your elements are 0, 1, 2, 3 and the Cartesian products are 00, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33. Does that make it clear what I mean by counting to 16 and converting into base 4?
    – Peter Taylor
    Apr 27 at 12:37










  • Yes, I get it! Thanks
    – Matthieu
    Apr 27 at 12:57














up vote
1
down vote



accepted











def generate_permutations(symbols, permutations_length):



These are not permutations. They are Cartesian products. In addition, calling the function generate_something suggests that what's returned might be a generator, which is probably what you want for a function which returns a stupendously large data structure, but is not what you get from this function.





 for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)



If you use extend instead of append then you give better hints as to how much underlying buffers might need to be increased:



 for tail in tails:
result.extend([symbol] + tail for symbol in symbols)



Performance: with an alphabet of size $L$ let $T_L(X)$ be the cost of calling this function for products of length $X$. Then $T_L(1) = L$ and $T_L(X+1) = T_L(X) + L L^X (X+2)$ assuming that constructing the list [symbol] + tail takes time proportional to its length and extending the list of results take amortised constant time per element added. This gives $Theta(L^XX)$ complexity.




The same complexity but much better memory efficiency can be achieved with a true generator approach which is based on the fact that all you really need to do is count to $L^X$ and convert into base $L$.






share|improve this answer





















  • Thanks a lot for this explanation! And I agree that generate_something was not the best way to name it. May I ask how you would do the same thing with a generator instead?
    – Matthieu
    Apr 27 at 11:55











  • @Matthieu: suppose that instead of 'a', 'b', 'c', 'd' your elements are 0, 1, 2, 3 and the Cartesian products are 00, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33. Does that make it clear what I mean by counting to 16 and converting into base 4?
    – Peter Taylor
    Apr 27 at 12:37










  • Yes, I get it! Thanks
    – Matthieu
    Apr 27 at 12:57












up vote
1
down vote



accepted







up vote
1
down vote



accepted







def generate_permutations(symbols, permutations_length):



These are not permutations. They are Cartesian products. In addition, calling the function generate_something suggests that what's returned might be a generator, which is probably what you want for a function which returns a stupendously large data structure, but is not what you get from this function.





 for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)



If you use extend instead of append then you give better hints as to how much underlying buffers might need to be increased:



 for tail in tails:
result.extend([symbol] + tail for symbol in symbols)



Performance: with an alphabet of size $L$ let $T_L(X)$ be the cost of calling this function for products of length $X$. Then $T_L(1) = L$ and $T_L(X+1) = T_L(X) + L L^X (X+2)$ assuming that constructing the list [symbol] + tail takes time proportional to its length and extending the list of results take amortised constant time per element added. This gives $Theta(L^XX)$ complexity.




The same complexity but much better memory efficiency can be achieved with a true generator approach which is based on the fact that all you really need to do is count to $L^X$ and convert into base $L$.






share|improve this answer














def generate_permutations(symbols, permutations_length):



These are not permutations. They are Cartesian products. In addition, calling the function generate_something suggests that what's returned might be a generator, which is probably what you want for a function which returns a stupendously large data structure, but is not what you get from this function.





 for symbol in symbols:
for tail in tails:
permutation = [symbol] + tail

permutations.append(permutation)



If you use extend instead of append then you give better hints as to how much underlying buffers might need to be increased:



 for tail in tails:
result.extend([symbol] + tail for symbol in symbols)



Performance: with an alphabet of size $L$ let $T_L(X)$ be the cost of calling this function for products of length $X$. Then $T_L(1) = L$ and $T_L(X+1) = T_L(X) + L L^X (X+2)$ assuming that constructing the list [symbol] + tail takes time proportional to its length and extending the list of results take amortised constant time per element added. This gives $Theta(L^XX)$ complexity.




The same complexity but much better memory efficiency can be achieved with a true generator approach which is based on the fact that all you really need to do is count to $L^X$ and convert into base $L$.







share|improve this answer













share|improve this answer



share|improve this answer











answered Apr 27 at 11:31









Peter Taylor

14k2454




14k2454











  • Thanks a lot for this explanation! And I agree that generate_something was not the best way to name it. May I ask how you would do the same thing with a generator instead?
    – Matthieu
    Apr 27 at 11:55











  • @Matthieu: suppose that instead of 'a', 'b', 'c', 'd' your elements are 0, 1, 2, 3 and the Cartesian products are 00, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33. Does that make it clear what I mean by counting to 16 and converting into base 4?
    – Peter Taylor
    Apr 27 at 12:37










  • Yes, I get it! Thanks
    – Matthieu
    Apr 27 at 12:57
















  • Thanks a lot for this explanation! And I agree that generate_something was not the best way to name it. May I ask how you would do the same thing with a generator instead?
    – Matthieu
    Apr 27 at 11:55











  • @Matthieu: suppose that instead of 'a', 'b', 'c', 'd' your elements are 0, 1, 2, 3 and the Cartesian products are 00, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33. Does that make it clear what I mean by counting to 16 and converting into base 4?
    – Peter Taylor
    Apr 27 at 12:37










  • Yes, I get it! Thanks
    – Matthieu
    Apr 27 at 12:57















Thanks a lot for this explanation! And I agree that generate_something was not the best way to name it. May I ask how you would do the same thing with a generator instead?
– Matthieu
Apr 27 at 11:55





Thanks a lot for this explanation! And I agree that generate_something was not the best way to name it. May I ask how you would do the same thing with a generator instead?
– Matthieu
Apr 27 at 11:55













@Matthieu: suppose that instead of 'a', 'b', 'c', 'd' your elements are 0, 1, 2, 3 and the Cartesian products are 00, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33. Does that make it clear what I mean by counting to 16 and converting into base 4?
– Peter Taylor
Apr 27 at 12:37




@Matthieu: suppose that instead of 'a', 'b', 'c', 'd' your elements are 0, 1, 2, 3 and the Cartesian products are 00, 01, 02, 03, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33. Does that make it clear what I mean by counting to 16 and converting into base 4?
– Peter Taylor
Apr 27 at 12:37












Yes, I get it! Thanks
– Matthieu
Apr 27 at 12:57




Yes, I get it! Thanks
– Matthieu
Apr 27 at 12:57












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193066%2falgorithmic-complexity-of-this-algorithm-to-find-all-ordered-permutations-of-len%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Chat program with C++ and SFML

Function to Return a JSON Like Objects Using VBA Collections and Arrays

Will my employers contract hold up in court?