更好的方法来实现count_permutations?

时间:2008-11-09 16:18:24

标签: c++ algorithm math

我需要一个函数count_permutations()来返回给定范围的排列数。 假设允许修改范围,并从第一个排列开始,我可以天真地实现这个,因为重复调用next_permutation(),如下所示:

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

是否有更快的方法不需要遍历所有排列来找到答案?它仍然可以假设输入可以被修改,并在第一个排列中开始,但显然如果可以在没有这些假设的情况下实现它也会很棒。

2 个答案:

答案 0 :(得分:9)

所有元素都是唯一的范围的排列数是n!其中n是范围的长度。

如果有重复的元素,你可以使用n!/(n_0!)...(n_m!),其中n_0 ... n_m是重复范围的长度。

所以例如[1,2,3]有3个! = 6个排列,而[1,2,2]有3!/ 2! = 3个排列。

编辑:更好的例子是[1,2,2,3,3,3],它有6!/ 2!3! = 60排列。

答案 1 :(得分:0)

在数学中,函数factorial!n表示n个元素的排列数。

正如Can Berg和Greg所建议的,如果集合中有重复的元素,考虑到它们,我们必须将阶乘除以每个难以区分的组的排列数(由相同元素组成的组)。

以下实现计算范围[first,end]中元素的排列数。范围不需要排序。

// 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;
}
相关问题