views:

226

answers:

2

I need a function count_permutations() that returns the number of permutations of a given range. Assuming that the range is allowed to be modified, and starts at the first permutation, I could naively implement this as repeated calls to next_permutation() as below:

template<class Ret, class Iter>
Ret count_permutations(Iter first, Iter last)
{
    Ret ret = 0;
    do {
        ++ret;
    } while (next_permutation(first, last));
    return ret;
}

Is there a faster way that doesn't require iterating through all the permutations to find the answer? It could still assume that the input can be modified, and starts in the first permutation, but obviously if it is possible to implement without those assumtions it'd be great too.

+9  A: 

The number of permutations for a range where all the elements are unique is n! where n is the length of the range.

If there are duplicate elements, you can use n!/(n_0!)...(n_m!) where n_0...n_m are the lengths of duplicate ranges.

So for example [1,2,3] has 3! = 6 permutations while [1,2,2] has 3!/2! = 3 permutations.

EDIT: A better example is [1,2,2,3,3,3] which has 6!/2!3! = 60 permutations.

Can Berk Güder
Ok so then if the range is sorted it would require one pass through the range to find all the sizes of duplicate ranges. Thanks.
Greg Rogers
For C++, yes, it would be easier if the range is sorted. For a dynamically-typed language like Python, even an unsorted list would do.Even if you have to sort the range, the time-complexity would be O(n log n + n) = O(n log n), not taking into account the factorial calculation.
Can Berk Güder
Note that 13! is too large to fit in an unsigned 32-bit integer, and 21! is too large to fit in an unsigned 64-bit integer. If your range has a non-trivial number of elements, you may need to use a bignum library to keep the math from overflowing.
bk1e
A: 

In math the function factorial !n represents the number of permutations of n elements.

As Can Berg and Greg suggested, if there are repeated elements in a set, to take them into account, we must divide the factorial by the number of permutations of each indistinguishable group (groups composed of identical elements).

The following implementation counts the number of permutations of the elements in the range [first, end). The range is not required to be sorted.

// generic factorial implementation...

int factorial(int number) {
    int temp;
    if(number <= 1) return 1;
    temp = number * factorial(number - 1);
    return temp;
}

template<class Ret, class Iter>
Ret count_permutations(Iter first, Iter end)
{
    std::map<typename Iter::value_type, int> counter;
    Iter it = first;
    for( ; it != end; ++it) {
        counter[*it]++;
    }

    int n = 0;
    typename std::map<typename Iter::value_type, int>::iterator mi = counter.begin();
    for(; mi != counter.end() ; mi++)
        if ( mi->second > 1 )
            n += factorial(mi->second);

   return factorial(std::distance(first,end))/n;
}
Nicola Bonelli
This doesn't account for repeated elements
Greg Rogers
Yes Greg you are right...
Nicola Bonelli