Search code examples
pythonpermutation

Getting count of permutations in a faster way


Using this code to get count of permutations is slow on big numbers as the partition part takes long time to calculate all the partitions for a number like 100 and because of all the partitions in the ram, it is very ram consuming. Any solution to get count of permutations in a faster way? Thanks.

If we have get_permutations_count(10,10) means all the permutations in the length of 10 using 10 distinct symbols and If we have get_permutations_count(10,1) means all the permutations in the length of 10 using 1 distinct symbol which going to be 10 as those permutations will be 0000000000 1111111111 2222222222 333333333 ... 9999999999.

from sympy.utilities.iterables import partitions
from sympy import factorial

def get_permutations_count(all_symbols_count, used_symbols_count):
    m = n = all_symbols_count
    r = n - used_symbols_count
    while True:
        result = 0
        for partition in partitions(r):
            length = 0
            if 2 * r > n:
                for k, v in partition.items():
                    length += (k + 1) * v
            if length > n:
                pass
            else:
                C = binomial(m, n - r)
                d = n - r
                for v in partition.values():
                    C *= binomial(d, v)
                    d -= v
                # permutations
                P = 1
                for k in partition.keys():
                    for v in range(partition[k]):
                        P *= factorial(k + 1)
                P = factorial(n) // P
                result += C * P
        return result

if __name__ == "__main__":
print(get_permutations_count(300, 270)) # takes long time to calculate
print(get_permutations_count(10, 9) # prints: 163296000
print(get_permutations_count(10, 10)) # prints: 3628800

Solution

  • Following this answer, you can find the derivation of efficient algorithms for counting the number of such permutation.

    It is achieved by using a generalization of the problem to count sequences of a length not necessarily equals to the size of the alphabet.

    from functools import lru_cache
    @lru_cache
    def get_permutations_count(n_symbols, length, distinct, used=0):
        '''
         - n_symbols: number of symbols in the alphabet
         - length: the number of symbols in each sequence
         - distinct: the number of distinct symbols in each sequence
        '''
        if distinct < 0:
            return 0
        if length == 0:
            return 1 if distinct == 0 else 0
        else:
            return \
              get_permutations_count(n_symbols, length-1, distinct-0, used+0) * used + \
              get_permutations_count(n_symbols, length-1, distinct-1, used+1) * (n_symbols - used)
    

    Then

    get_permutations_count(n_symbols=300, length=300, distinct=270)
    

    runs in ~0.5 second giving the answer

    2729511887951350984580070745513114266766906881300774347439917775
    7093985721949669285469996223829969654724957176705978029888262889
    8157939885553971500652353177628564896814078569667364402373549268
    5524290993833663948683375995196081654415976659499171897405039547
    1546236260377859451955180752885715923847446106509971875543496023
    2494854876774756172488117802642800540206851318332940739395445903
    6305051887120804168979339693187702655904071331731936748927759927
    3688881301614948043182289382736687065840703041231428800720854767
    0713406956719647313048146023960093662879015837313428567467555885
    3564982943420444850950866922223974844727296000000000000000000000
    000000000000000000000000000000000000000000000000