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

Clash 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.
python algorithm reinventing-the-wheel combinatorics complexity
add a comment |Â
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.
python algorithm reinventing-the-wheel combinatorics complexity
"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
add a comment |Â
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.
python algorithm reinventing-the-wheel combinatorics complexity
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.
python algorithm reinventing-the-wheel combinatorics complexity
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
add a comment |Â
"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
add a comment |Â
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$.
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 are0, 1, 2, 3and the Cartesian products are00, 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
add a comment |Â
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$.
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 are0, 1, 2, 3and the Cartesian products are00, 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
add a comment |Â
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$.
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 are0, 1, 2, 3and the Cartesian products are00, 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
add a comment |Â
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$.
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$.
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 are0, 1, 2, 3and the Cartesian products are00, 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
add a comment |Â
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 are0, 1, 2, 3and the Cartesian products are00, 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
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
"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