permutations with unique values

前端 未结 19 1422
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-22 01:53

itertools.permutations generates where its elements are treated as unique based on their position, not on their value. So basically I want to avoid duplicates like this:

相关标签:
19条回答
  • 2020-11-22 02:40

    What about

    np.unique(itertools.permutations([1, 1, 1]))
    

    The problem is the permutations are now rows of a Numpy array, thus using more memory, but you can cycle through them as before

    perms = np.unique(itertools.permutations([1, 1, 1]))
    for p in perms:
        print p
    
    0 讨论(0)
  • 2020-11-22 02:42
    class unique_element:
        def __init__(self,value,occurrences):
            self.value = value
            self.occurrences = occurrences
    
    def perm_unique(elements):
        eset=set(elements)
        listunique = [unique_element(i,elements.count(i)) for i in eset]
        u=len(elements)
        return perm_unique_helper(listunique,[0]*u,u-1)
    
    def perm_unique_helper(listunique,result_list,d):
        if d < 0:
            yield tuple(result_list)
        else:
            for i in listunique:
                if i.occurrences > 0:
                    result_list[d]=i.value
                    i.occurrences-=1
                    for g in  perm_unique_helper(listunique,result_list,d-1):
                        yield g
                    i.occurrences+=1
    
    
    
    
    a = list(perm_unique([1,1,2]))
    print(a)
    

    result:

    [(2, 1, 1), (1, 2, 1), (1, 1, 2)]
    

    EDIT (how this works):

    I rewrote the above program to be longer but more readable.

    I usually have a hard time explaining how something works, but let me try. In order to understand how this works, you have to understand a similar but simpler program that would yield all permutations with repetitions.

    def permutations_with_replacement(elements,n):
        return permutations_helper(elements,[0]*n,n-1)#this is generator
    
    def permutations_helper(elements,result_list,d):
        if d<0:
            yield tuple(result_list)
        else:
            for i in elements:
                result_list[d]=i
                all_permutations = permutations_helper(elements,result_list,d-1)#this is generator
                for g in all_permutations:
                    yield g
    

    This program is obviously much simpler: d stands for depth in permutations_helper and has two functions. One function is the stopping condition of our recursive algorithm, and the other is for the result list that is passed around.

    Instead of returning each result, we yield it. If there were no function/operator yield we would have to push the result in some queue at the point of the stopping condition. But this way, once the stopping condition is met, the result is propagated through all stacks up to the caller. That is the purpose of
    for g in perm_unique_helper(listunique,result_list,d-1): yield g so each result is propagated up to caller.

    Back to the original program: we have a list of unique elements. Before we can use each element, we have to check how many of them are still available to push onto result_list. Working with this program is very similar to permutations_with_replacement. The difference is that each element cannot be repeated more times than it is in perm_unique_helper.

    0 讨论(0)
  • 2020-11-22 02:42

    It sound like you are looking for itertools.combinations() docs.python.org

    list(itertools.combinations([1, 1, 1],3))
    [(1, 1, 1)]
    
    0 讨论(0)
  • 2020-11-22 02:43

    Because sometimes new questions are marked as duplicates and their authors are referred to this question it may be important to mention that sympy has an iterator for this purpose.

    >>> from sympy.utilities.iterables import multiset_permutations
    >>> list(multiset_permutations([1,1,1]))
    [[1, 1, 1]]
    >>> list(multiset_permutations([1,1,2]))
    [[1, 1, 2], [1, 2, 1], [2, 1, 1]]
    
    0 讨论(0)
  • 2020-11-22 02:44

    To generate unique permutations of ["A","B","C","D"] I use the following:

    from itertools import combinations,chain
    
    l = ["A","B","C","D"]
    combs = (combinations(l, r) for r in range(1, len(l) + 1))
    list_combinations = list(chain.from_iterable(combs))
    

    Which generates:

    [('A',),
     ('B',),
     ('C',),
     ('D',),
     ('A', 'B'),
     ('A', 'C'),
     ('A', 'D'),
     ('B', 'C'),
     ('B', 'D'),
     ('C', 'D'),
     ('A', 'B', 'C'),
     ('A', 'B', 'D'),
     ('A', 'C', 'D'),
     ('B', 'C', 'D'),
     ('A', 'B', 'C', 'D')]
    

    Notice, duplicates are not created (e.g. items in combination with D are not generated, as they already exist).

    Example: This can then be used in generating terms of higher or lower order for OLS models via data in a Pandas dataframe.

    import statsmodels.formula.api as smf
    import pandas as pd
    
    # create some data
    pd_dataframe = pd.Dataframe(somedata)
    response_column = "Y"
    
    # generate combinations of column/variable names
    l = [col for col in pd_dataframe.columns if col!=response_column]
    combs = (combinations(l, r) for r in range(1, len(l) + 1))
    list_combinations = list(chain.from_iterable(combs))
    
    # generate OLS input string
    formula_base = '{} ~ '.format(response_column)
    list_for_ols = [":".join(list(item)) for item in list_combinations]
    string_for_ols = formula_base + ' + '.join(list_for_ols)
    

    Creates...

    Y ~ A + B + C + D + A:B + A:C + A:D + B:C + B:D + C:D + A:B:C + A:B:D + A:C:D + B:C:D + A:B:C:D'
    

    Which can then be piped to your OLS regression

    model = smf.ols(string_for_ols, pd_dataframe).fit()
    model.summary()
    
    0 讨论(0)
  • 2020-11-22 02:47

    You can make a function that uses collections.Counter to get unique items and their counts from the given sequence, and uses itertools.combinations to pick combinations of indices for each unique item in each recursive call, and map the indices back to a list when all indices are picked:

    from collections import Counter
    from itertools import combinations
    def unique_permutations(seq):
        def index_permutations(counts, index_pool):
            if not counts:
                yield {}
                return
            (item, count), *rest = counts.items()
            rest = dict(rest)
            for indices in combinations(index_pool, count):
                mapping = dict.fromkeys(indices, item)
                for others in index_permutations(rest, index_pool.difference(indices)):
                    yield {**mapping, **others}
        indices = set(range(len(seq)))
        for mapping in index_permutations(Counter(seq), indices):
            yield [mapping[i] for i in indices]
    

    so that [''.join(i) for i in unique_permutations('moon')] returns:

    ['moon', 'mono', 'mnoo', 'omon', 'omno', 'nmoo', 'oomn', 'onmo', 'nomo', 'oonm', 'onom', 'noom']
    
    0 讨论(0)
提交回复
热议问题