Compute all the permutations for a given vector of integersIterative version of a permutations algorithmGenerating all possible permutations of the stringGenerate all permutationsPrinting all the permutations of a string in alphabetical orderNecklace counting problem-with consecutive prime constraintDisplaying all permutations of a stringGenerating all permutations of a sequenceGet all points within given radius for a given vectorAll permutations of integer vectorMedian of a Vector assignmentGenerate all possible permutations of a string in C++
Don’t seats that recline flat defeat the purpose of having seatbelts?
Map of water taps to fill bottles
Could the terminal length of components like resistors be reduced?
Pre-plastic human skin alternative
"Whatever a Russian does, they end up making the Kalashnikov gun"? Are there any similar proverbs in English?
How to pronounce 'c++' in Spanish
Can I grease a crank spindle/bracket without disassembling the crank set?
What's the polite way to say "I need to urinate"?
What are the steps to solving this definite integral?
Can an Area of Effect spell cast outside a Prismatic Wall extend inside it?
Get consecutive integer number ranges from list of int
Why was the Spitfire's elliptical wing almost uncopied by other aircraft of World War 2?
Betweenness centrality formula
I preordered a game on my Xbox while on the home screen of my friend's account. Which of us owns the game?
What happens to Mjolnir (Thor's hammer) at the end of Endgame?
How come there are so many candidates for the 2020 Democratic party presidential nomination?
Why didn't the Space Shuttle bounce back into space as many times as possible so as to lose a lot of kinetic energy up there?
Relationship between strut and baselineskip
Checks user level and limit the data before saving it to mongoDB
Dynamic SOQL query relationship with field visibility for Users
Phrase for the opposite of "foolproof"
What term is being referred to with "reflected-sound-of-underground-spirits"?
Why do games have consumables?
Does tea made with boiling water cool faster than tea made with boiled (but still hot) water?
Compute all the permutations for a given vector of integers
Iterative version of a permutations algorithmGenerating all possible permutations of the stringGenerate all permutationsPrinting all the permutations of a string in alphabetical orderNecklace counting problem-with consecutive prime constraintDisplaying all permutations of a stringGenerating all permutations of a sequenceGet all points within given radius for a given vectorAll permutations of integer vectorMedian of a Vector assignmentGenerate all possible permutations of a string in C++
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
The task is to compute all the permutations for a given vector of integers (but of course the specific integer type is not relevant for the solution)
The strategy is based on recursion + iterations
At each recursion, the state consists of
the root sequence
a
which is the set of elements already placedthe remaining elements set
b
which is the set of elements still to be placed
Inside the recursion, a loop places the N(i)
(with i
recursion index and) remaining elements producing the same amount of new root sequences and a new recursion is started so that N(i+1)=N(i)-1
hence meaning the overall complexity is O(N!)
as expected
The recursion ends when there are no more elements to place hence b.empty()
is true
Each recursion set ends with a valid sequence hence they are all merged together in a final list of sequences
Here is my CPP solution
#include <iostream>
#include <vector>
using namespace std;
vector<int> remove_item (const vector<int>& a, const unsigned int id)
vector<int> res;
for(unsigned int i=0; i<a.size(); ++i) if(i!=id) res.push_back(a[i]);
return res;
vector<int> add_item(const vector<int>& a, const int b)
vector<int> res=a;
res.push_back(b);
return res;
vector< vector<int> > merge(const vector< vector<int> >& a, const vector< vector<int> >& b)
vector< vector<int> > res=a;
for(const auto& e : b) res.push_back(e);
return res;
vector< vector<int> > permutations(const vector<int>& b, const vector<int>& a=)
if(b.empty()) return a ;
vector< vector<int> > res;
for(unsigned int i=0; i<b.size(); ++i) res=merge(res, permutations(remove_item(b,i), add_item(a, b[i])));
return res;
int main()
// your code goes here
auto res = permutations(1,2,3,4,5);
cout << "Sol Num = " << res.size() << endl;
for(const auto& a : res)
for(const auto& b : a) cout << to_string(b) << " ";
cout << endl;
return 0;
c++ reinventing-the-wheel combinatorics
New contributor
$endgroup$
add a comment |
$begingroup$
The task is to compute all the permutations for a given vector of integers (but of course the specific integer type is not relevant for the solution)
The strategy is based on recursion + iterations
At each recursion, the state consists of
the root sequence
a
which is the set of elements already placedthe remaining elements set
b
which is the set of elements still to be placed
Inside the recursion, a loop places the N(i)
(with i
recursion index and) remaining elements producing the same amount of new root sequences and a new recursion is started so that N(i+1)=N(i)-1
hence meaning the overall complexity is O(N!)
as expected
The recursion ends when there are no more elements to place hence b.empty()
is true
Each recursion set ends with a valid sequence hence they are all merged together in a final list of sequences
Here is my CPP solution
#include <iostream>
#include <vector>
using namespace std;
vector<int> remove_item (const vector<int>& a, const unsigned int id)
vector<int> res;
for(unsigned int i=0; i<a.size(); ++i) if(i!=id) res.push_back(a[i]);
return res;
vector<int> add_item(const vector<int>& a, const int b)
vector<int> res=a;
res.push_back(b);
return res;
vector< vector<int> > merge(const vector< vector<int> >& a, const vector< vector<int> >& b)
vector< vector<int> > res=a;
for(const auto& e : b) res.push_back(e);
return res;
vector< vector<int> > permutations(const vector<int>& b, const vector<int>& a=)
if(b.empty()) return a ;
vector< vector<int> > res;
for(unsigned int i=0; i<b.size(); ++i) res=merge(res, permutations(remove_item(b,i), add_item(a, b[i])));
return res;
int main()
// your code goes here
auto res = permutations(1,2,3,4,5);
cout << "Sol Num = " << res.size() << endl;
for(const auto& a : res)
for(const auto& b : a) cout << to_string(b) << " ";
cout << endl;
return 0;
c++ reinventing-the-wheel combinatorics
New contributor
$endgroup$
$begingroup$
Not only the time complexity, but also the memory complexity is $O(n!)$ – because that's the size of your output. For $n$ greater than tiny this may be a serious disadvantage...
$endgroup$
– CiaPan
2 days ago
$begingroup$
The number of permutations is N! which means eventually you need to store all of them so you need O(N!) memory, unless you are allowed to print them as soon as you discover them
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
That's it: unless. You never said they must be delivered all at once. So it's a valid supposition you can find them one by one and utilize (for example: print) iteratively...
$endgroup$
– CiaPan
2 days ago
add a comment |
$begingroup$
The task is to compute all the permutations for a given vector of integers (but of course the specific integer type is not relevant for the solution)
The strategy is based on recursion + iterations
At each recursion, the state consists of
the root sequence
a
which is the set of elements already placedthe remaining elements set
b
which is the set of elements still to be placed
Inside the recursion, a loop places the N(i)
(with i
recursion index and) remaining elements producing the same amount of new root sequences and a new recursion is started so that N(i+1)=N(i)-1
hence meaning the overall complexity is O(N!)
as expected
The recursion ends when there are no more elements to place hence b.empty()
is true
Each recursion set ends with a valid sequence hence they are all merged together in a final list of sequences
Here is my CPP solution
#include <iostream>
#include <vector>
using namespace std;
vector<int> remove_item (const vector<int>& a, const unsigned int id)
vector<int> res;
for(unsigned int i=0; i<a.size(); ++i) if(i!=id) res.push_back(a[i]);
return res;
vector<int> add_item(const vector<int>& a, const int b)
vector<int> res=a;
res.push_back(b);
return res;
vector< vector<int> > merge(const vector< vector<int> >& a, const vector< vector<int> >& b)
vector< vector<int> > res=a;
for(const auto& e : b) res.push_back(e);
return res;
vector< vector<int> > permutations(const vector<int>& b, const vector<int>& a=)
if(b.empty()) return a ;
vector< vector<int> > res;
for(unsigned int i=0; i<b.size(); ++i) res=merge(res, permutations(remove_item(b,i), add_item(a, b[i])));
return res;
int main()
// your code goes here
auto res = permutations(1,2,3,4,5);
cout << "Sol Num = " << res.size() << endl;
for(const auto& a : res)
for(const auto& b : a) cout << to_string(b) << " ";
cout << endl;
return 0;
c++ reinventing-the-wheel combinatorics
New contributor
$endgroup$
The task is to compute all the permutations for a given vector of integers (but of course the specific integer type is not relevant for the solution)
The strategy is based on recursion + iterations
At each recursion, the state consists of
the root sequence
a
which is the set of elements already placedthe remaining elements set
b
which is the set of elements still to be placed
Inside the recursion, a loop places the N(i)
(with i
recursion index and) remaining elements producing the same amount of new root sequences and a new recursion is started so that N(i+1)=N(i)-1
hence meaning the overall complexity is O(N!)
as expected
The recursion ends when there are no more elements to place hence b.empty()
is true
Each recursion set ends with a valid sequence hence they are all merged together in a final list of sequences
Here is my CPP solution
#include <iostream>
#include <vector>
using namespace std;
vector<int> remove_item (const vector<int>& a, const unsigned int id)
vector<int> res;
for(unsigned int i=0; i<a.size(); ++i) if(i!=id) res.push_back(a[i]);
return res;
vector<int> add_item(const vector<int>& a, const int b)
vector<int> res=a;
res.push_back(b);
return res;
vector< vector<int> > merge(const vector< vector<int> >& a, const vector< vector<int> >& b)
vector< vector<int> > res=a;
for(const auto& e : b) res.push_back(e);
return res;
vector< vector<int> > permutations(const vector<int>& b, const vector<int>& a=)
if(b.empty()) return a ;
vector< vector<int> > res;
for(unsigned int i=0; i<b.size(); ++i) res=merge(res, permutations(remove_item(b,i), add_item(a, b[i])));
return res;
int main()
// your code goes here
auto res = permutations(1,2,3,4,5);
cout << "Sol Num = " << res.size() << endl;
for(const auto& a : res)
for(const auto& b : a) cout << to_string(b) << " ";
cout << endl;
return 0;
c++ reinventing-the-wheel combinatorics
c++ reinventing-the-wheel combinatorics
New contributor
New contributor
edited Apr 23 at 16:15
Deduplicator
12k1950
12k1950
New contributor
asked Apr 23 at 10:17
Nicola BerniniNicola Bernini
1494
1494
New contributor
New contributor
$begingroup$
Not only the time complexity, but also the memory complexity is $O(n!)$ – because that's the size of your output. For $n$ greater than tiny this may be a serious disadvantage...
$endgroup$
– CiaPan
2 days ago
$begingroup$
The number of permutations is N! which means eventually you need to store all of them so you need O(N!) memory, unless you are allowed to print them as soon as you discover them
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
That's it: unless. You never said they must be delivered all at once. So it's a valid supposition you can find them one by one and utilize (for example: print) iteratively...
$endgroup$
– CiaPan
2 days ago
add a comment |
$begingroup$
Not only the time complexity, but also the memory complexity is $O(n!)$ – because that's the size of your output. For $n$ greater than tiny this may be a serious disadvantage...
$endgroup$
– CiaPan
2 days ago
$begingroup$
The number of permutations is N! which means eventually you need to store all of them so you need O(N!) memory, unless you are allowed to print them as soon as you discover them
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
That's it: unless. You never said they must be delivered all at once. So it's a valid supposition you can find them one by one and utilize (for example: print) iteratively...
$endgroup$
– CiaPan
2 days ago
$begingroup$
Not only the time complexity, but also the memory complexity is $O(n!)$ – because that's the size of your output. For $n$ greater than tiny this may be a serious disadvantage...
$endgroup$
– CiaPan
2 days ago
$begingroup$
Not only the time complexity, but also the memory complexity is $O(n!)$ – because that's the size of your output. For $n$ greater than tiny this may be a serious disadvantage...
$endgroup$
– CiaPan
2 days ago
$begingroup$
The number of permutations is N! which means eventually you need to store all of them so you need O(N!) memory, unless you are allowed to print them as soon as you discover them
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
The number of permutations is N! which means eventually you need to store all of them so you need O(N!) memory, unless you are allowed to print them as soon as you discover them
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
That's it: unless. You never said they must be delivered all at once. So it's a valid supposition you can find them one by one and utilize (for example: print) iteratively...
$endgroup$
– CiaPan
2 days ago
$begingroup$
That's it: unless. You never said they must be delivered all at once. So it's a valid supposition you can find them one by one and utilize (for example: print) iteratively...
$endgroup$
– CiaPan
2 days ago
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
A cleaner solution is to trust the standard library and try to re-use the generic components already available there. Your problem is solved by std::next_permutation, so you can proceed along the lines of:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
std::vector<int> v = 1, 2, 3, 4, 5 ;
do
for (auto e : v)
std::cout << e << " ";
std::cout << "n";
while (std::next_permutation(v.begin(), v.end()));
For pedagogical purposes, if you wanted to keep your current structure, you could also use standard functions there. In particular, remove_item
and merge
could be rewritten to:
std::vector<int> remove_item(const std::vector<int>& a, int id)
assert(id >= 0 && id < a.size());
std::vector<int> res(a.begin(), a.begin() + id);
res.insert(res.end(), a.begin() + id + 1, a.end());
return res;
std::vector<std::vector<int> > merge(const std::vector<std::vector<int> >& a, const std::vector<std::vector<int> >& b)
std::vector<std::vector<int> > res(a);
std::copy(b.begin(), b.end(), std::back_inserter(res));
return res;
Whatever you do, as general comments:
Avoid writing
using namespace std;
.Don't write
std::endl
whenn
will do.You don't need
std::to_string
, just printb
.You are more likely to make mistakes when you put multiple statements on the same line. So instead of writing
for(...) if(...) v.push_back(x);
just writefor(...)
if(...)
v.push_back(x);
This also improves readability.
$endgroup$
1
$begingroup$
Sure, but using the standard library makes it trivial My goal was to develop a working algo
$endgroup$
– Nicola Bernini
Apr 23 at 13:57
$begingroup$
@NicolaBernini Indeed, I hope that nobody is discouraged from reviewing your code beyond my answer.
$endgroup$
– Juho
Apr 23 at 13:58
1
$begingroup$
@NicolaBernini Trust Juho, you will be able to write better algorithms if you learn the standard library.
$endgroup$
– WooWapDaBug
Apr 23 at 15:02
$begingroup$
@WooWapDaBug I already know it (at least at some extent, there is always room for improvement) but I hope it is clear it was not the point of the exercise
$endgroup$
– Nicola Bernini
Apr 23 at 15:15
$begingroup$
@NicolaBernini It wasn't, but now the relevant tag is added so it is.
$endgroup$
– Mast
Apr 23 at 17:09
add a comment |
$begingroup$
I understand the need to reinvent the wheel but in this case you reinvented an other kind of wheel: functional-style combinatorics aren't very well suited to C++ and the high-performance / low-memory usage it is well known for. I mean, that's a bike wheel for a car.
Now if you want to reinvent the C++ wheel, the best thing would be to re-implement std::next_permutation
: an algorithm that does its work incrementally, in place, and with iterators (meaning that you can compute the permutations of strings, arrays, double-linked lists and everything that exposes bidirectional iterators).
Interestingly, there's an example of implementation on cppreference.com:
template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
if (first == last) return false;
BidirIt i = last;
if (first == --i) return false;
while (true)
BidirIt i1, i2;
i1 = i;
if (*--i < *i1)
i2 = last;
while (!(*i < *--i2))
;
std::iter_swap(i, i2);
std::reverse(i1, last);
return true;
if (i == first)
std::reverse(first, last);
return false;
This implementation is a good example of "C++-sprinkled" C code. It's rather elegant but difficult to understand. If you reverse-engineer it though, you'll see it's quite simple:
first, beginning from the end, find the first adjacent items in increasing order. Let's call the lesser item's position the permutation point. If there are none, that was the last permutation: reverse and return false;
then, also beginning from the end, find the first item whose value is superior to that of the permutation point. Swap those two, reverse the range
(permutation_point, last)
and return true.
Now we're ready to reinvent a C++ wheel the C++ way:
#include <algorithm>
#include <iterator>
template <typename Iterator>
bool permute(Iterator first, Iterator last) std::next(first) == last) return false;
// first step: first adjacent elements in increasing order, starting from the end
const auto r_first = std::reverse_iterator(last);
const auto r_last = std::reverse_iterator(first);
auto position = std::adjacent_find(r_first, r_last, [](auto lhs, auto rhs)
return lhs > rhs;
);
// check if it was the last permutation
if (position == r_last)
std::reverse(first, last);
return false;
++position; // advance position to the lesser item
// second step: swap the permutation point and the first greater value from the end
std::iter_swap(position, std::find_if(r_first, position, [position](auto value)
return value > *position;
));
std::reverse(r_first, position);
return true;
$endgroup$
$begingroup$
I understand your point and thanks for sharing it, but I have to do bias disclosure now: I (strongly) favour functional style over imperative (which does not mean I’m a purist, otherwise I would not have used loops, I’m just trying to take the best of both worlds) Certainly it can have a performance cost (depends how smart the compiler is) but I think it is not necessary for me to list the joys functional programming
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
@NicolaBernini: well, you can always putnext_permutation
into a functional shell (like you do withinsert
,push_back
, etc. in your helper functions). I also like functional programming a lot, and have tried a few times to adopt functional idioms in C++ but there's always a point where you understand C++ won't ever be Haskell. But yeah, sure!
$endgroup$
– papagaga
2 days ago
$begingroup$
This is the strategy I adopt typically for my hybrid functional style, but not in this case as I was trying to reimplement next_permutation but in production code that’s what I tend to do. C++ does not need be Haskell, but some C++ programmer would certainly take benefit from some exposure to Haskell :)
$endgroup$
– Nicola Bernini
2 days ago
add a comment |
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: "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',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
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
);
);
Nicola Bernini is a new contributor. Be nice, and check out our Code of Conduct.
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
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217939%2fcompute-all-the-permutations-for-a-given-vector-of-integers%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
$begingroup$
A cleaner solution is to trust the standard library and try to re-use the generic components already available there. Your problem is solved by std::next_permutation, so you can proceed along the lines of:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
std::vector<int> v = 1, 2, 3, 4, 5 ;
do
for (auto e : v)
std::cout << e << " ";
std::cout << "n";
while (std::next_permutation(v.begin(), v.end()));
For pedagogical purposes, if you wanted to keep your current structure, you could also use standard functions there. In particular, remove_item
and merge
could be rewritten to:
std::vector<int> remove_item(const std::vector<int>& a, int id)
assert(id >= 0 && id < a.size());
std::vector<int> res(a.begin(), a.begin() + id);
res.insert(res.end(), a.begin() + id + 1, a.end());
return res;
std::vector<std::vector<int> > merge(const std::vector<std::vector<int> >& a, const std::vector<std::vector<int> >& b)
std::vector<std::vector<int> > res(a);
std::copy(b.begin(), b.end(), std::back_inserter(res));
return res;
Whatever you do, as general comments:
Avoid writing
using namespace std;
.Don't write
std::endl
whenn
will do.You don't need
std::to_string
, just printb
.You are more likely to make mistakes when you put multiple statements on the same line. So instead of writing
for(...) if(...) v.push_back(x);
just writefor(...)
if(...)
v.push_back(x);
This also improves readability.
$endgroup$
1
$begingroup$
Sure, but using the standard library makes it trivial My goal was to develop a working algo
$endgroup$
– Nicola Bernini
Apr 23 at 13:57
$begingroup$
@NicolaBernini Indeed, I hope that nobody is discouraged from reviewing your code beyond my answer.
$endgroup$
– Juho
Apr 23 at 13:58
1
$begingroup$
@NicolaBernini Trust Juho, you will be able to write better algorithms if you learn the standard library.
$endgroup$
– WooWapDaBug
Apr 23 at 15:02
$begingroup$
@WooWapDaBug I already know it (at least at some extent, there is always room for improvement) but I hope it is clear it was not the point of the exercise
$endgroup$
– Nicola Bernini
Apr 23 at 15:15
$begingroup$
@NicolaBernini It wasn't, but now the relevant tag is added so it is.
$endgroup$
– Mast
Apr 23 at 17:09
add a comment |
$begingroup$
A cleaner solution is to trust the standard library and try to re-use the generic components already available there. Your problem is solved by std::next_permutation, so you can proceed along the lines of:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
std::vector<int> v = 1, 2, 3, 4, 5 ;
do
for (auto e : v)
std::cout << e << " ";
std::cout << "n";
while (std::next_permutation(v.begin(), v.end()));
For pedagogical purposes, if you wanted to keep your current structure, you could also use standard functions there. In particular, remove_item
and merge
could be rewritten to:
std::vector<int> remove_item(const std::vector<int>& a, int id)
assert(id >= 0 && id < a.size());
std::vector<int> res(a.begin(), a.begin() + id);
res.insert(res.end(), a.begin() + id + 1, a.end());
return res;
std::vector<std::vector<int> > merge(const std::vector<std::vector<int> >& a, const std::vector<std::vector<int> >& b)
std::vector<std::vector<int> > res(a);
std::copy(b.begin(), b.end(), std::back_inserter(res));
return res;
Whatever you do, as general comments:
Avoid writing
using namespace std;
.Don't write
std::endl
whenn
will do.You don't need
std::to_string
, just printb
.You are more likely to make mistakes when you put multiple statements on the same line. So instead of writing
for(...) if(...) v.push_back(x);
just writefor(...)
if(...)
v.push_back(x);
This also improves readability.
$endgroup$
1
$begingroup$
Sure, but using the standard library makes it trivial My goal was to develop a working algo
$endgroup$
– Nicola Bernini
Apr 23 at 13:57
$begingroup$
@NicolaBernini Indeed, I hope that nobody is discouraged from reviewing your code beyond my answer.
$endgroup$
– Juho
Apr 23 at 13:58
1
$begingroup$
@NicolaBernini Trust Juho, you will be able to write better algorithms if you learn the standard library.
$endgroup$
– WooWapDaBug
Apr 23 at 15:02
$begingroup$
@WooWapDaBug I already know it (at least at some extent, there is always room for improvement) but I hope it is clear it was not the point of the exercise
$endgroup$
– Nicola Bernini
Apr 23 at 15:15
$begingroup$
@NicolaBernini It wasn't, but now the relevant tag is added so it is.
$endgroup$
– Mast
Apr 23 at 17:09
add a comment |
$begingroup$
A cleaner solution is to trust the standard library and try to re-use the generic components already available there. Your problem is solved by std::next_permutation, so you can proceed along the lines of:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
std::vector<int> v = 1, 2, 3, 4, 5 ;
do
for (auto e : v)
std::cout << e << " ";
std::cout << "n";
while (std::next_permutation(v.begin(), v.end()));
For pedagogical purposes, if you wanted to keep your current structure, you could also use standard functions there. In particular, remove_item
and merge
could be rewritten to:
std::vector<int> remove_item(const std::vector<int>& a, int id)
assert(id >= 0 && id < a.size());
std::vector<int> res(a.begin(), a.begin() + id);
res.insert(res.end(), a.begin() + id + 1, a.end());
return res;
std::vector<std::vector<int> > merge(const std::vector<std::vector<int> >& a, const std::vector<std::vector<int> >& b)
std::vector<std::vector<int> > res(a);
std::copy(b.begin(), b.end(), std::back_inserter(res));
return res;
Whatever you do, as general comments:
Avoid writing
using namespace std;
.Don't write
std::endl
whenn
will do.You don't need
std::to_string
, just printb
.You are more likely to make mistakes when you put multiple statements on the same line. So instead of writing
for(...) if(...) v.push_back(x);
just writefor(...)
if(...)
v.push_back(x);
This also improves readability.
$endgroup$
A cleaner solution is to trust the standard library and try to re-use the generic components already available there. Your problem is solved by std::next_permutation, so you can proceed along the lines of:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
std::vector<int> v = 1, 2, 3, 4, 5 ;
do
for (auto e : v)
std::cout << e << " ";
std::cout << "n";
while (std::next_permutation(v.begin(), v.end()));
For pedagogical purposes, if you wanted to keep your current structure, you could also use standard functions there. In particular, remove_item
and merge
could be rewritten to:
std::vector<int> remove_item(const std::vector<int>& a, int id)
assert(id >= 0 && id < a.size());
std::vector<int> res(a.begin(), a.begin() + id);
res.insert(res.end(), a.begin() + id + 1, a.end());
return res;
std::vector<std::vector<int> > merge(const std::vector<std::vector<int> >& a, const std::vector<std::vector<int> >& b)
std::vector<std::vector<int> > res(a);
std::copy(b.begin(), b.end(), std::back_inserter(res));
return res;
Whatever you do, as general comments:
Avoid writing
using namespace std;
.Don't write
std::endl
whenn
will do.You don't need
std::to_string
, just printb
.You are more likely to make mistakes when you put multiple statements on the same line. So instead of writing
for(...) if(...) v.push_back(x);
just writefor(...)
if(...)
v.push_back(x);
This also improves readability.
edited 2 days ago
Toby Speight
27.9k742120
27.9k742120
answered Apr 23 at 10:39
JuhoJuho
1,746712
1,746712
1
$begingroup$
Sure, but using the standard library makes it trivial My goal was to develop a working algo
$endgroup$
– Nicola Bernini
Apr 23 at 13:57
$begingroup$
@NicolaBernini Indeed, I hope that nobody is discouraged from reviewing your code beyond my answer.
$endgroup$
– Juho
Apr 23 at 13:58
1
$begingroup$
@NicolaBernini Trust Juho, you will be able to write better algorithms if you learn the standard library.
$endgroup$
– WooWapDaBug
Apr 23 at 15:02
$begingroup$
@WooWapDaBug I already know it (at least at some extent, there is always room for improvement) but I hope it is clear it was not the point of the exercise
$endgroup$
– Nicola Bernini
Apr 23 at 15:15
$begingroup$
@NicolaBernini It wasn't, but now the relevant tag is added so it is.
$endgroup$
– Mast
Apr 23 at 17:09
add a comment |
1
$begingroup$
Sure, but using the standard library makes it trivial My goal was to develop a working algo
$endgroup$
– Nicola Bernini
Apr 23 at 13:57
$begingroup$
@NicolaBernini Indeed, I hope that nobody is discouraged from reviewing your code beyond my answer.
$endgroup$
– Juho
Apr 23 at 13:58
1
$begingroup$
@NicolaBernini Trust Juho, you will be able to write better algorithms if you learn the standard library.
$endgroup$
– WooWapDaBug
Apr 23 at 15:02
$begingroup$
@WooWapDaBug I already know it (at least at some extent, there is always room for improvement) but I hope it is clear it was not the point of the exercise
$endgroup$
– Nicola Bernini
Apr 23 at 15:15
$begingroup$
@NicolaBernini It wasn't, but now the relevant tag is added so it is.
$endgroup$
– Mast
Apr 23 at 17:09
1
1
$begingroup$
Sure, but using the standard library makes it trivial My goal was to develop a working algo
$endgroup$
– Nicola Bernini
Apr 23 at 13:57
$begingroup$
Sure, but using the standard library makes it trivial My goal was to develop a working algo
$endgroup$
– Nicola Bernini
Apr 23 at 13:57
$begingroup$
@NicolaBernini Indeed, I hope that nobody is discouraged from reviewing your code beyond my answer.
$endgroup$
– Juho
Apr 23 at 13:58
$begingroup$
@NicolaBernini Indeed, I hope that nobody is discouraged from reviewing your code beyond my answer.
$endgroup$
– Juho
Apr 23 at 13:58
1
1
$begingroup$
@NicolaBernini Trust Juho, you will be able to write better algorithms if you learn the standard library.
$endgroup$
– WooWapDaBug
Apr 23 at 15:02
$begingroup$
@NicolaBernini Trust Juho, you will be able to write better algorithms if you learn the standard library.
$endgroup$
– WooWapDaBug
Apr 23 at 15:02
$begingroup$
@WooWapDaBug I already know it (at least at some extent, there is always room for improvement) but I hope it is clear it was not the point of the exercise
$endgroup$
– Nicola Bernini
Apr 23 at 15:15
$begingroup$
@WooWapDaBug I already know it (at least at some extent, there is always room for improvement) but I hope it is clear it was not the point of the exercise
$endgroup$
– Nicola Bernini
Apr 23 at 15:15
$begingroup$
@NicolaBernini It wasn't, but now the relevant tag is added so it is.
$endgroup$
– Mast
Apr 23 at 17:09
$begingroup$
@NicolaBernini It wasn't, but now the relevant tag is added so it is.
$endgroup$
– Mast
Apr 23 at 17:09
add a comment |
$begingroup$
I understand the need to reinvent the wheel but in this case you reinvented an other kind of wheel: functional-style combinatorics aren't very well suited to C++ and the high-performance / low-memory usage it is well known for. I mean, that's a bike wheel for a car.
Now if you want to reinvent the C++ wheel, the best thing would be to re-implement std::next_permutation
: an algorithm that does its work incrementally, in place, and with iterators (meaning that you can compute the permutations of strings, arrays, double-linked lists and everything that exposes bidirectional iterators).
Interestingly, there's an example of implementation on cppreference.com:
template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
if (first == last) return false;
BidirIt i = last;
if (first == --i) return false;
while (true)
BidirIt i1, i2;
i1 = i;
if (*--i < *i1)
i2 = last;
while (!(*i < *--i2))
;
std::iter_swap(i, i2);
std::reverse(i1, last);
return true;
if (i == first)
std::reverse(first, last);
return false;
This implementation is a good example of "C++-sprinkled" C code. It's rather elegant but difficult to understand. If you reverse-engineer it though, you'll see it's quite simple:
first, beginning from the end, find the first adjacent items in increasing order. Let's call the lesser item's position the permutation point. If there are none, that was the last permutation: reverse and return false;
then, also beginning from the end, find the first item whose value is superior to that of the permutation point. Swap those two, reverse the range
(permutation_point, last)
and return true.
Now we're ready to reinvent a C++ wheel the C++ way:
#include <algorithm>
#include <iterator>
template <typename Iterator>
bool permute(Iterator first, Iterator last) std::next(first) == last) return false;
// first step: first adjacent elements in increasing order, starting from the end
const auto r_first = std::reverse_iterator(last);
const auto r_last = std::reverse_iterator(first);
auto position = std::adjacent_find(r_first, r_last, [](auto lhs, auto rhs)
return lhs > rhs;
);
// check if it was the last permutation
if (position == r_last)
std::reverse(first, last);
return false;
++position; // advance position to the lesser item
// second step: swap the permutation point and the first greater value from the end
std::iter_swap(position, std::find_if(r_first, position, [position](auto value)
return value > *position;
));
std::reverse(r_first, position);
return true;
$endgroup$
$begingroup$
I understand your point and thanks for sharing it, but I have to do bias disclosure now: I (strongly) favour functional style over imperative (which does not mean I’m a purist, otherwise I would not have used loops, I’m just trying to take the best of both worlds) Certainly it can have a performance cost (depends how smart the compiler is) but I think it is not necessary for me to list the joys functional programming
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
@NicolaBernini: well, you can always putnext_permutation
into a functional shell (like you do withinsert
,push_back
, etc. in your helper functions). I also like functional programming a lot, and have tried a few times to adopt functional idioms in C++ but there's always a point where you understand C++ won't ever be Haskell. But yeah, sure!
$endgroup$
– papagaga
2 days ago
$begingroup$
This is the strategy I adopt typically for my hybrid functional style, but not in this case as I was trying to reimplement next_permutation but in production code that’s what I tend to do. C++ does not need be Haskell, but some C++ programmer would certainly take benefit from some exposure to Haskell :)
$endgroup$
– Nicola Bernini
2 days ago
add a comment |
$begingroup$
I understand the need to reinvent the wheel but in this case you reinvented an other kind of wheel: functional-style combinatorics aren't very well suited to C++ and the high-performance / low-memory usage it is well known for. I mean, that's a bike wheel for a car.
Now if you want to reinvent the C++ wheel, the best thing would be to re-implement std::next_permutation
: an algorithm that does its work incrementally, in place, and with iterators (meaning that you can compute the permutations of strings, arrays, double-linked lists and everything that exposes bidirectional iterators).
Interestingly, there's an example of implementation on cppreference.com:
template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
if (first == last) return false;
BidirIt i = last;
if (first == --i) return false;
while (true)
BidirIt i1, i2;
i1 = i;
if (*--i < *i1)
i2 = last;
while (!(*i < *--i2))
;
std::iter_swap(i, i2);
std::reverse(i1, last);
return true;
if (i == first)
std::reverse(first, last);
return false;
This implementation is a good example of "C++-sprinkled" C code. It's rather elegant but difficult to understand. If you reverse-engineer it though, you'll see it's quite simple:
first, beginning from the end, find the first adjacent items in increasing order. Let's call the lesser item's position the permutation point. If there are none, that was the last permutation: reverse and return false;
then, also beginning from the end, find the first item whose value is superior to that of the permutation point. Swap those two, reverse the range
(permutation_point, last)
and return true.
Now we're ready to reinvent a C++ wheel the C++ way:
#include <algorithm>
#include <iterator>
template <typename Iterator>
bool permute(Iterator first, Iterator last) std::next(first) == last) return false;
// first step: first adjacent elements in increasing order, starting from the end
const auto r_first = std::reverse_iterator(last);
const auto r_last = std::reverse_iterator(first);
auto position = std::adjacent_find(r_first, r_last, [](auto lhs, auto rhs)
return lhs > rhs;
);
// check if it was the last permutation
if (position == r_last)
std::reverse(first, last);
return false;
++position; // advance position to the lesser item
// second step: swap the permutation point and the first greater value from the end
std::iter_swap(position, std::find_if(r_first, position, [position](auto value)
return value > *position;
));
std::reverse(r_first, position);
return true;
$endgroup$
$begingroup$
I understand your point and thanks for sharing it, but I have to do bias disclosure now: I (strongly) favour functional style over imperative (which does not mean I’m a purist, otherwise I would not have used loops, I’m just trying to take the best of both worlds) Certainly it can have a performance cost (depends how smart the compiler is) but I think it is not necessary for me to list the joys functional programming
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
@NicolaBernini: well, you can always putnext_permutation
into a functional shell (like you do withinsert
,push_back
, etc. in your helper functions). I also like functional programming a lot, and have tried a few times to adopt functional idioms in C++ but there's always a point where you understand C++ won't ever be Haskell. But yeah, sure!
$endgroup$
– papagaga
2 days ago
$begingroup$
This is the strategy I adopt typically for my hybrid functional style, but not in this case as I was trying to reimplement next_permutation but in production code that’s what I tend to do. C++ does not need be Haskell, but some C++ programmer would certainly take benefit from some exposure to Haskell :)
$endgroup$
– Nicola Bernini
2 days ago
add a comment |
$begingroup$
I understand the need to reinvent the wheel but in this case you reinvented an other kind of wheel: functional-style combinatorics aren't very well suited to C++ and the high-performance / low-memory usage it is well known for. I mean, that's a bike wheel for a car.
Now if you want to reinvent the C++ wheel, the best thing would be to re-implement std::next_permutation
: an algorithm that does its work incrementally, in place, and with iterators (meaning that you can compute the permutations of strings, arrays, double-linked lists and everything that exposes bidirectional iterators).
Interestingly, there's an example of implementation on cppreference.com:
template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
if (first == last) return false;
BidirIt i = last;
if (first == --i) return false;
while (true)
BidirIt i1, i2;
i1 = i;
if (*--i < *i1)
i2 = last;
while (!(*i < *--i2))
;
std::iter_swap(i, i2);
std::reverse(i1, last);
return true;
if (i == first)
std::reverse(first, last);
return false;
This implementation is a good example of "C++-sprinkled" C code. It's rather elegant but difficult to understand. If you reverse-engineer it though, you'll see it's quite simple:
first, beginning from the end, find the first adjacent items in increasing order. Let's call the lesser item's position the permutation point. If there are none, that was the last permutation: reverse and return false;
then, also beginning from the end, find the first item whose value is superior to that of the permutation point. Swap those two, reverse the range
(permutation_point, last)
and return true.
Now we're ready to reinvent a C++ wheel the C++ way:
#include <algorithm>
#include <iterator>
template <typename Iterator>
bool permute(Iterator first, Iterator last) std::next(first) == last) return false;
// first step: first adjacent elements in increasing order, starting from the end
const auto r_first = std::reverse_iterator(last);
const auto r_last = std::reverse_iterator(first);
auto position = std::adjacent_find(r_first, r_last, [](auto lhs, auto rhs)
return lhs > rhs;
);
// check if it was the last permutation
if (position == r_last)
std::reverse(first, last);
return false;
++position; // advance position to the lesser item
// second step: swap the permutation point and the first greater value from the end
std::iter_swap(position, std::find_if(r_first, position, [position](auto value)
return value > *position;
));
std::reverse(r_first, position);
return true;
$endgroup$
I understand the need to reinvent the wheel but in this case you reinvented an other kind of wheel: functional-style combinatorics aren't very well suited to C++ and the high-performance / low-memory usage it is well known for. I mean, that's a bike wheel for a car.
Now if you want to reinvent the C++ wheel, the best thing would be to re-implement std::next_permutation
: an algorithm that does its work incrementally, in place, and with iterators (meaning that you can compute the permutations of strings, arrays, double-linked lists and everything that exposes bidirectional iterators).
Interestingly, there's an example of implementation on cppreference.com:
template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
if (first == last) return false;
BidirIt i = last;
if (first == --i) return false;
while (true)
BidirIt i1, i2;
i1 = i;
if (*--i < *i1)
i2 = last;
while (!(*i < *--i2))
;
std::iter_swap(i, i2);
std::reverse(i1, last);
return true;
if (i == first)
std::reverse(first, last);
return false;
This implementation is a good example of "C++-sprinkled" C code. It's rather elegant but difficult to understand. If you reverse-engineer it though, you'll see it's quite simple:
first, beginning from the end, find the first adjacent items in increasing order. Let's call the lesser item's position the permutation point. If there are none, that was the last permutation: reverse and return false;
then, also beginning from the end, find the first item whose value is superior to that of the permutation point. Swap those two, reverse the range
(permutation_point, last)
and return true.
Now we're ready to reinvent a C++ wheel the C++ way:
#include <algorithm>
#include <iterator>
template <typename Iterator>
bool permute(Iterator first, Iterator last) std::next(first) == last) return false;
// first step: first adjacent elements in increasing order, starting from the end
const auto r_first = std::reverse_iterator(last);
const auto r_last = std::reverse_iterator(first);
auto position = std::adjacent_find(r_first, r_last, [](auto lhs, auto rhs)
return lhs > rhs;
);
// check if it was the last permutation
if (position == r_last)
std::reverse(first, last);
return false;
++position; // advance position to the lesser item
// second step: swap the permutation point and the first greater value from the end
std::iter_swap(position, std::find_if(r_first, position, [position](auto value)
return value > *position;
));
std::reverse(r_first, position);
return true;
answered 2 days ago
papagagapapagaga
5,097522
5,097522
$begingroup$
I understand your point and thanks for sharing it, but I have to do bias disclosure now: I (strongly) favour functional style over imperative (which does not mean I’m a purist, otherwise I would not have used loops, I’m just trying to take the best of both worlds) Certainly it can have a performance cost (depends how smart the compiler is) but I think it is not necessary for me to list the joys functional programming
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
@NicolaBernini: well, you can always putnext_permutation
into a functional shell (like you do withinsert
,push_back
, etc. in your helper functions). I also like functional programming a lot, and have tried a few times to adopt functional idioms in C++ but there's always a point where you understand C++ won't ever be Haskell. But yeah, sure!
$endgroup$
– papagaga
2 days ago
$begingroup$
This is the strategy I adopt typically for my hybrid functional style, but not in this case as I was trying to reimplement next_permutation but in production code that’s what I tend to do. C++ does not need be Haskell, but some C++ programmer would certainly take benefit from some exposure to Haskell :)
$endgroup$
– Nicola Bernini
2 days ago
add a comment |
$begingroup$
I understand your point and thanks for sharing it, but I have to do bias disclosure now: I (strongly) favour functional style over imperative (which does not mean I’m a purist, otherwise I would not have used loops, I’m just trying to take the best of both worlds) Certainly it can have a performance cost (depends how smart the compiler is) but I think it is not necessary for me to list the joys functional programming
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
@NicolaBernini: well, you can always putnext_permutation
into a functional shell (like you do withinsert
,push_back
, etc. in your helper functions). I also like functional programming a lot, and have tried a few times to adopt functional idioms in C++ but there's always a point where you understand C++ won't ever be Haskell. But yeah, sure!
$endgroup$
– papagaga
2 days ago
$begingroup$
This is the strategy I adopt typically for my hybrid functional style, but not in this case as I was trying to reimplement next_permutation but in production code that’s what I tend to do. C++ does not need be Haskell, but some C++ programmer would certainly take benefit from some exposure to Haskell :)
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
I understand your point and thanks for sharing it, but I have to do bias disclosure now: I (strongly) favour functional style over imperative (which does not mean I’m a purist, otherwise I would not have used loops, I’m just trying to take the best of both worlds) Certainly it can have a performance cost (depends how smart the compiler is) but I think it is not necessary for me to list the joys functional programming
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
I understand your point and thanks for sharing it, but I have to do bias disclosure now: I (strongly) favour functional style over imperative (which does not mean I’m a purist, otherwise I would not have used loops, I’m just trying to take the best of both worlds) Certainly it can have a performance cost (depends how smart the compiler is) but I think it is not necessary for me to list the joys functional programming
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
@NicolaBernini: well, you can always put
next_permutation
into a functional shell (like you do with insert
, push_back
, etc. in your helper functions). I also like functional programming a lot, and have tried a few times to adopt functional idioms in C++ but there's always a point where you understand C++ won't ever be Haskell. But yeah, sure!$endgroup$
– papagaga
2 days ago
$begingroup$
@NicolaBernini: well, you can always put
next_permutation
into a functional shell (like you do with insert
, push_back
, etc. in your helper functions). I also like functional programming a lot, and have tried a few times to adopt functional idioms in C++ but there's always a point where you understand C++ won't ever be Haskell. But yeah, sure!$endgroup$
– papagaga
2 days ago
$begingroup$
This is the strategy I adopt typically for my hybrid functional style, but not in this case as I was trying to reimplement next_permutation but in production code that’s what I tend to do. C++ does not need be Haskell, but some C++ programmer would certainly take benefit from some exposure to Haskell :)
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
This is the strategy I adopt typically for my hybrid functional style, but not in this case as I was trying to reimplement next_permutation but in production code that’s what I tend to do. C++ does not need be Haskell, but some C++ programmer would certainly take benefit from some exposure to Haskell :)
$endgroup$
– Nicola Bernini
2 days ago
add a comment |
Nicola Bernini is a new contributor. Be nice, and check out our Code of Conduct.
Nicola Bernini is a new contributor. Be nice, and check out our Code of Conduct.
Nicola Bernini is a new contributor. Be nice, and check out our Code of Conduct.
Nicola Bernini is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- 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.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
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
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217939%2fcompute-all-the-permutations-for-a-given-vector-of-integers%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
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
Required, but never shown
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
Required, but never shown
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
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
$begingroup$
Not only the time complexity, but also the memory complexity is $O(n!)$ – because that's the size of your output. For $n$ greater than tiny this may be a serious disadvantage...
$endgroup$
– CiaPan
2 days ago
$begingroup$
The number of permutations is N! which means eventually you need to store all of them so you need O(N!) memory, unless you are allowed to print them as soon as you discover them
$endgroup$
– Nicola Bernini
2 days ago
$begingroup$
That's it: unless. You never said they must be delivered all at once. So it's a valid supposition you can find them one by one and utilize (for example: print) iteratively...
$endgroup$
– CiaPan
2 days ago