1

For some algorithm I have to call a function f on all permutations of the n-element set of integers ranging from 1 to n. Finally, I am interested in the 100 permutations yielding the smallest f-value.

The best solution I was able to work out was the following one. But it has a serious drawback. While n increases fPerm can consume a large amount of memory.

What is the best solution to trim fPerm, so that it keeps only the best 100 solutions found so far?

#include <vector>
#include <set>
#include <algorithm>
#include <numeric>
#include <boost/random.hpp>

boost::random::mt19937 rng;

// Actually, f is a very complex function, that I'm not posting here.
double f(const std::vector<int>& perm) {
    boost::random::uniform_real_distribution<> gen;
    return gen(rng);
}

typedef std::pair<std::vector<int>, double> TValue;

void main(int argc, int args) {
    auto comp = [](const TValue& v1, const TValue& v2) { return v1.second < v2.second; };
    std::set<TValue, decltype(comp) > fPerm(comp);
    int n = 7;
    std::vector<int> perm(n);
    std::iota(perm.begin(), perm.end(),1);
    do {
        fPerm.insert(TValue(perm, f(perm)));
    } while (std::next_permutation(perm.begin(), perm.end()));

    // Get first smallest 100 values, if there are such many.
    int m = 100 < fPerm.size() ? 100 : fPerm.size();
    auto iterEnd = fPerm.begin();
    std::advance(iterEnd, m);
    for (auto iter = fPerm.begin(); iter != iterEnd; iter++) {
        std::cout << iter->second << std::endl;
    }
}
Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • 1
    Why do you keep all permutations? Keep the 100 smallest solutions and the relevant permutations. Replace one of the smallest solutions with its permutation if a new smallest is found. I would keep the solutions in a sorted dequeue, added the new solution to the lower end of the queue, removed the sol – GMichael Jun 24 '16 at 09:01
  • @GMichael you should throw out the largest of the 100 when you are keeping the smallest 100 and find a new smallest. – arekolek Jun 24 '16 at 09:04
  • Thanks for the fast reply. Actually, this is what I want to do. But how will I do it exactly and what will be the best and shortest solution. – Aleph0 Jun 24 '16 at 09:04
  • Possible duplicate of [How to create a min heap of fixed size using std::priority\_queue?](http://stackoverflow.com/questions/33180609/how-to-create-a-min-heap-of-fixed-size-using-stdpriority-queue) – arekolek Jun 24 '16 at 09:04
  • 2
    @arekolek No, I would do something simpler. I would keep them in a sorted container, add new smallest and remove largest from the kept values. I would also use the kept values as key for storing the permutation in another container. – GMichael Jun 24 '16 at 09:06
  • @GMichael that sounds exactly like the solution I have linked to. Doesn't it? – arekolek Jun 24 '16 at 09:09
  • @arekolek Yes, I saw it after I wrote my comments. Timing is everything :-) – GMichael Jun 24 '16 at 09:10
  • So, is there still a problem with your updated code? If so, what is it? – arekolek Jun 24 '16 at 10:19
  • No. Many thanks. My revised solution works for me. Many thanks to all of you. – Aleph0 Jun 24 '16 at 11:11
  • In such case, you might consider [posting it as an answer](http://stackoverflow.com/help/self-answer) to your question, instead of editing it. – arekolek Jun 24 '16 at 11:54
  • Oh, thanks. I'll do that. – Aleph0 Jun 24 '16 at 11:55

1 Answers1

0

I revised my solution above by implementing a kind of trim function erasing the largest element of the set. As pointed out below the usage of a std::priority_queue might be even more shorter.

#include <vector>
#include <set>
#include <algorithm>
#include <numeric>
#include <boost/random.hpp>

boost::random::mt19937 rng;

double f(const std::vector<int>& perm) {
    boost::random::uniform_real_distribution<> gen;
    return gen(rng);
}

typedef std::pair<std::vector<int>, double> TValue;

void main(int argc, int args) {
    auto comp = [](const TValue& v1, const TValue& v2) { return v1.second < v2.second; };
    std::set<TValue, decltype(comp) > fPerm(comp);
    int n = 7;
    std::vector<int> perm(7);
    std::iota(perm.begin(), perm.end(),1);
    do {
        fPerm.insert(TValue(perm, f(perm)));
        if (fPerm.size() > 100) {
            fPerm.erase(*fPerm.rbegin());
        }
    } while (std::next_permutation(perm.begin(), perm.end()));

    // Get first smallest 100 values, if there are such many.
    int m = 100 < fPerm.size() ? 100 : fPerm.size();
    auto iterEnd = fPerm.begin();
    std::advance(iterEnd, m);
    for (auto iter = fPerm.begin(); iter != iterEnd; iter++) {
        std::cout << iter->second << std::endl;
    }
}
Aleph0
  • 5,816
  • 4
  • 29
  • 80