Query: python how to generate only half of the permutations

Relevant code:
def distinct_permutations(iterable):
    """Yield successive distinct permutations of the elements in *iterable*.

        >>> sorted(distinct_permutations([1, 0, 1]))
        [(0, 1, 1), (1, 0, 1), (1, 1, 0)]

    Equivalent to ``set(permutations(iterable))``, except duplicates are not
    generated and thrown away. For larger input sequences this is much more
    efficient.

    Duplicate permutations arise when there are duplicated elements in the
    input iterable. The number of items returned is
    `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of
    items input, and each `x_i` is the count of a distinct item in the input
    sequence.

    """
    def make_new_permutations(permutations, e):
        """Internal helper function.
        The output permutations are built up by adding element *e* to the
        current *permutations* at every possible position.
        The key idea is to keep repeated elements (reverse) ordered:
        if e1 == e2 and e1 is before e2 in the iterable, then all permutations
        with e1 before e2 are ignored.

        """
        for permutation in permutations:
            for j in range(len(permutation)):
                yield permutation[:j] + [e] + permutation[j:]
                if permutation[j] == e:
                    break
            else:
                yield permutation + [e]

    permutations = [[]]
    for e in iterable:
        permutations = make_new_permutations(permutations, e)

    return (tuple(t) for t in permutations)

Similar but not relevant code:
def circ_permutation(items):
    """Calculate the circular permutation for a given list of items."""
    permutations = []
    for i in range(len(items)):
        permutations.append(items[i:] + items[:i])
    return permutations

Similar but not relevant code:
def endless_permutations(N, random_state=None):
    """
    Generate an endless sequence of random integers from permutations of the
    set [0, ..., N).

    If we call this N times, we will sweep through the entire set without
    replacement, on the (N+1)th call a new permutation will be created, etc.

    Parameters
    ----------
    N: int
        the length of the set
    random_state: int or RandomState, optional
        random seed

    Yields
    ------
    int:
        a random int from the set [0, ..., N)
    """
    generator = check_random_state(random_state)
    while True:
        batch_inds = generator.permutation(N)
        for b in batch_inds:
            yield b

Similar but not relevant code:
def trivial_partition(set_):
    """Returns a parition of given set into 1-element subsets.

    :return: Trivial partition of given set, i.e. iterable containing disjoint
             1-element sets, each consisting of a single element
             from given set
    """
    ensure_countable(set_)

    result = ((x,) for x in set_)
    return _harmonize_subset_types(set_, result)

Similar but not relevant code:
def product(*args, **kwargs):
    """ Yields all permutations with replacement:
        list(product("cat", repeat=2)) => 
        [("c", "c"), 
         ("c", "a"), 
         ("c", "t"), 
         ("a", "c"), 
         ("a", "a"), 
         ("a", "t"), 
         ("t", "c"), 
         ("t", "a"), 
         ("t", "t")]
    """
    p = [[]]
    for iterable in map(tuple, args) * kwargs.get("repeat", 1):
        p = [x + [y] for x in p for y in iterable]
    for p in p:
        yield tuple(p)

Similar but not relevant code:
def unique_inverse(item_list):
    """
    Like np.unique(item_list, return_inverse=True)
    """
    import utool as ut
    unique_items = ut.unique(item_list)
    inverse = list_alignment(unique_items, item_list)
    return unique_items, inverse