pythonlistpermutation

Permutation of list in python with one or multiple fixed items


I have a list of a = [5,6,7,9] I want all the possible permutations, with one or more entry fixed.

e.g. If I want to fix third element 7, then in all permutations I want to see 7 in the third place of the list.

The code I wrote works well.

from itertools import permutations

a = [5,6,7,9]

fixed_element_index =  2 # I want to keep 7 at the same location and permutate only [5,6, ,9]

a_moveable = a[:fixed_element_index]+a[fixed_element_index+1:]

perm = permutations(a_moveable)

perm_list = []

for x in perm:
    perm_list.append(list(x))


for y in perm_list:
    y.insert(fixed_element_index,a[fixed_element_index])
print(perm_list)

My main problem is if the list one is long and I want multiple indexes to be fixed then what is the best way to do that.

the output is like this [[5, 6, 7, 9], [5, 9, 7, 6], [6, 5, 7, 9], [6, 9, 7, 5], [9, 5, 7, 6], [9, 6, 7, 5]]


Solution

  • As already mentioned in the comments, I would propose the following pragmatic approach:

    from itertools import permutations
     
    def fixed_permutations(values: list, fixed_pos: list[int] | None = None) -> list[list]:
        """
        Produces a list of lists with all possible permutations of the given values,
        except for those at the given fixed positions, which remain at their indices.
        
        :param values: list of values to be permuted
        :param fixed_pos: optional list of positions/indices to remain unpermuted
        :return: list of lists of the resulting permutations
        """
        fixed_pos = [] if fixed_pos is None else fixed_pos
        # Collect the values that are *not* to be kept at a fixed position
        permuted_values = [v for i, v in enumerate(values) if i not in fixed_pos]
        # Calculate all permutations
        all_permutations = [list(p) for p in permutations(permuted_values)]
        # Add fixed positions back in (in-place)
        for current_permutation in all_permutations:
            for fixed_position in fixed_pos:
                current_permutation.insert(fixed_position, values[fixed_position])
        return all_permutations
     
    print(fixed_permutations(["a", "b", "c", "d", "e"], fixed_pos=[1, 3]))
    # [['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'e', 'd', 'c'], ['c', 'b', 'a', 'd', 'e'],
    #  ['c', 'b', 'e', 'd', 'a'], ['e', 'b', 'a', 'd', 'c'], ['e', 'b', 'c', 'd', 'a']]
    

    It follows the idea of luk2302's comment: Put aside the values that are not supposed to be part of the permutation, permute the others (using itertools.permutations()), then reinsert the unpermuted values.

    As it is currently written, the proposed fixed_permutations() function consumes all results of itertools.permutations() eagerly. We could adjust that by not creating the all_permutations list but adding the values back into each individual permutation on-the-fly and then yielding the individual results rather than returning all at once. Also, as it is currently implemented, it might not be the most efficient approach, given that we have to add values back in for each individual result of itertools.permutations(). I do not see an easy solution to the latter, other than completely implementing the permutation approach from scratch.