Search code examples
pythonpython-itertoolscombinatoricspython-collections

Combinations with Replacement and maximal Occurrence Constraint


from itertools import *
import collections
for i in combinations_with_replacement(['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'],15):
    b = (''.join(i))
    freq = collections.Counter(b)
    for k in freq:
        if freq [k] < 5:
            print(k)

this code most print chars what count if less than 5

what i try do , cheek if at string from join at fly if there is repeated any of characters les than x times at any possition of that string and print strings only what true to that.

Problem is no mater what i try do , or its print all and ignore if ... or print notting. how do it right , or maybe at python exist simple solution ?

Result most be as example les than 5

False - fffaaffbbdd ( repeat 5 titemes f)
False - fffffaaaaac ( repeat 5 times a and f)
True -  aaabbbccc11 ( no any character repeated more than 4 times )

More clear explain qustion - filter all string with characters more than x repetions before give to next function. As examble - there is simple print that strings , and not print strings what not at rule.


Solution

  • a more constructive approach (meaning: i do not iterate over all possible combinations - i construct the valid combinations directly).

    you need to have sympy installed for this to work.

    in the example i only use the elements "abcdef" and restrict the repetitions to be strictly smaller than MAX = 4. i fix the length of the strings to be output at M = 6.

    i start by getting all the partitions of M with restricted repetitions k=MAX - 1 and not constisting of more than m=N parts. i immediately convert those to a list:

    {3: 2} [3, 3, 0, 0, 0, 0]
    {3: 1, 2: 1, 1: 1} [3, 2, 1, 0, 0, 0]
    {3: 1, 1: 3} [3, 1, 1, 1, 0, 0]
    {2: 3} [2, 2, 2, 0, 0, 0]
    {2: 2, 1: 2} [2, 2, 1, 1, 0, 0]
    {2: 1, 1: 4} [2, 1, 1, 1, 1, 0]
    {1: 6} [1, 1, 1, 1, 1, 1]
    

    of those lists i iterate over the multiset permutations - i mean those to represent the elements that i select and how often they are repeated: e.g:

    [2, 1, 2, 0, 0, 1] -> "aabccf"  # 2*"a", 1*"b", ..., 0*"e", 1*"f"
    

    the result you want is then the multiset permutation of those strings.

    from sympy.utilities.iterables import multiset_permutations, partitions
    
    MAX = 4  # (all counts < MAX)
    elements = "abcdef"
    N = len(elements)
    M = 6  # output length
    
    
    def dict_to_list(dct, N):
        ret = [0] * N
        j = 0
        for k, v in dct.items():
            ret[j:j + v] = [k] * v
            j += v
        return ret
    
    
    for dct in partitions(M, k=MAX - 1, m=N):
        lst = dict_to_list(dct, N)
        for part in multiset_permutations(lst):
            el = ''.join(n * v for n, v in zip(part, elements))
            for msp in multiset_permutations(el):
                print(''.join(msp))
    

    for your case you'd then need to change:

    MAX = 5  # (all counts < MAX)
    elements = "0123456789abcdef"
    M = 15  # output length
    

    but the complexity of that is huge (but way better that the one of the original approach)!