Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 生成没有相邻相等元素的列表的所有排列_Python_Algorithm_Combinatorics - Fatal编程技术网

Python 生成没有相邻相等元素的列表的所有排列

Python 生成没有相邻相等元素的列表的所有排列,python,algorithm,combinatorics,Python,Algorithm,Combinatorics,当我们对列表排序时,比如 a = [1,2,3,3,2,2,1] sorted(a) => [1, 1, 2, 2, 2, 3, 3] 相等的元素在结果列表中总是相邻的 我如何才能完成相反的任务——洗牌列表,使相等的元素永远不会(或尽可能少地)相邻 例如,对于上面的列表,可能的解决方案之一是 p = [1,3,2,3,2,1,2] 更正式地说,给定一个列表a,生成它的一个排列p,使成对数p[i]==p[i+1] 因为列表很大,所以不能生成和过滤所有排列 附加问题:如何有效地生成所有此类

当我们对列表排序时,比如

a = [1,2,3,3,2,2,1]
sorted(a) => [1, 1, 2, 2, 2, 3, 3]
相等的元素在结果列表中总是相邻的

我如何才能完成相反的任务——洗牌列表,使相等的元素永远不会(或尽可能少地)相邻

例如,对于上面的列表,可能的解决方案之一是

p = [1,3,2,3,2,1,2]
更正式地说,给定一个列表
a
,生成它的一个排列
p
,使成对数
p[i]==p[i+1]

因为列表很大,所以不能生成和过滤所有排列

附加问题:如何有效地生成所有此类排列

这是我用来测试解决方案的代码:

UPD:在这里选择获胜者是一个艰难的选择,因为很多人都给出了极好的答案,并提供了能够完美地生成最佳排列的函数。David还回答了奖金问题(生成所有排列)。David关于正确性证明的其他要点

@VincentvanderWeele的代码似乎是最快的。

伪代码:

  • 对列表排序
  • 循环排序列表的前半部分,并填充结果列表的所有偶数索引
  • 循环排序列表的后半部分,并填充结果列表的所有奇数索引
  • 如果有一半以上的输入由同一个元素组成,那么您只有
    p[i]==p[i+1]
    ,在这种情况下,除了将同一个元素放在连续的点中(根据皮江洞原理),别无选择


    正如评论中所指出的,如果其中一个元素至少出现
    n/2
    次(或
    n/2+1
    表示奇数
    n
    ;这将推广到
    (n+1)/2)
    表示偶数和奇数),则该方法可能会出现过多的冲突。最多有两个这样的元素,如果有两个,算法就可以正常工作。唯一有问题的情况是,有一个元素至少有一半的时间出现。我们可以简单地通过找到元素并首先处理它来解决这个问题

    我对python的了解还不够,无法正确编写这篇文章,因此我冒昧地从github复制了OP对以前版本的实现:

    # Sort the list
    a = sorted(lst)
    
    # Put the element occurring more than half of the times in front (if needed)
    n = len(a)
    m = (n + 1) // 2
    for i in range(n - m + 1):
        if a[i] == a[i + m - 1]:
            a = a[i:] + a[:i]
            break
    
    result = [None] * n
    
    # Loop over the first half of the sorted list and fill all even indices of the result list
    for i, elt in enumerate(a[:m]):
        result[2*i] = elt
    
    # Loop over the second half of the sorted list and fill all odd indices of the result list
    for i, elt in enumerate(a[m:]):
        result[2*i+1] = elt
    
    return result
    

    下面是一个很好的算法:

  • 首先,计算所有数字出现的频率。把答案放在地图上

  • 对该地图进行排序,使最常出现的数字排在第一位

  • 答案的第一个数字是已排序地图中的第一个数字

  • 请看地图,第一张现在要小一点


  • 如果要提高效率,请寻找提高排序步骤效率的方法

    其思想是将元素从最常见到最不常见进行排序,取最常见的元素,减少其计数并将其放回列表中,保持降序(但尽可能避免将最后使用的元素放在第一位,以防止重复)

    这可以使用和实现:

    例子
    在python中,您可以执行以下操作

    假设您有一个排序列表
    l
    ,您可以执行以下操作:

    length = len(l)
    odd_ind = length%2
    odd_half = (length - odd_ind)/2
    for i in range(odd_half)[::2]:
        my_list[i], my_list[odd_half+odd_ind+i] = my_list[odd_half+odd_ind+i], my_list[i]
    
    这些只是就地操作,因此应该相当快(
    O(N)
    )。 请注意,您将从
    l[i]==l[i+1]
    转换为
    l[i]==l[i+2]
    ,因此您最终得到的顺序不是随机的,但从我对问题的理解来看,您所寻找的不是随机性

    的思想是将排序的列表分割在中间,然后交换这两个部分中的其他元素。

    对于
    l=[1,1,1,2,2,3,3,4,4,5,5]
    这导致
    l=[3,1,4,2,5,1,3,1,4,2,5]

    当一个元素的丰度大于或等于列表长度的一半时,该方法无法去除所有
    l[i]==l[i+1]

    只要最频繁元素的丰度小于列表大小的一半,上述方法就可以正常工作,以下函数还处理极限情况(以一个问题命名),其中从第一个元素开始的所有其他元素都必须是最丰富的元素:

    def no_adjacent(my_list):
        my_list.sort()
        length = len(my_list)
        odd_ind = length%2
        odd_half = (length - odd_ind)/2
        for i in range(odd_half)[::2]:
            my_list[i], my_list[odd_half+odd_ind+i] = my_list[odd_half+odd_ind+i], my_list[i]
    
        #this is just for the limit case where the abundance of the most frequent is half of the list length
        if max([my_list.count(val) for val in set(my_list)]) + 1 - odd_ind > odd_half:
            max_val = my_list[0]
            max_count = my_list.count(max_val)
            for val in set(my_list):
                if my_list.count(val) > max_count:
                   max_val = val
                   max_count = my_list.count(max_val)
            while max_val in my_list:
                my_list.remove(max_val)
            out = [max_val]
            max_count -= 1
            for val in my_list:
                out.append(val)
                if max_count:
                    out.append(max_val)
                    max_count -= 1
            if max_count:
                print 'this is not working'
                return my_list
                #raise Exception('not possible')
            return out
        else:
            return my_list
    
    您可以使用递归回溯算法生成所有“完全未排序”排列(相邻位置没有两个相等的元素)。事实上,生成所有排列的唯一区别在于跟踪最后一个数字,并相应地排除一些解决方案:

    def取消排序(lst,last=None):
    如果是lst:
    对于枚举中的i,e(lst):
    如果e!=最后:
    对于反排序中的perm(lst[:i]+lst[i+1:],e):
    产量[e]+perm
    其他:
    收益率[]
    
    请注意,在这种形式下,函数的效率不是很高,因为它会创建很多子列表。此外,我们可以通过首先查看最受限制的数字(计数最高的数字)来加快速度。这里有一个更有效的版本,它只使用数字的
    计数

    def unsort_生成器(lst,sort=False):
    计数=集合。计数器(lst)
    def unsort_内部(剩余,最后=无):
    如果剩余值>0:
    #首先是最受约束的,还是为了漂亮的打印而排序?
    items=已排序(counts.items()),如果sort-else计数。最常见()
    对于项目中的n、c:
    如果n!=最后和c>0:
    计数[n]=1#更新计数
    对于unsort_inner中的perm(剩余-1,n):
    产量[n]+perm
    计数[n]+=1#恢复计数
    其他:
    收益率[]
    返回unsort_内部(透镜(lst))
    
    您可以使用此选项仅生成
    下一个
    完美排列,或者生成包含所有排列的
    列表。但是请注意,如果没有完全未排序的排列,那么这个生成器将因此产生yiel
    
    length = len(l)
    odd_ind = length%2
    odd_half = (length - odd_ind)/2
    for i in range(odd_half)[::2]:
        my_list[i], my_list[odd_half+odd_ind+i] = my_list[odd_half+odd_ind+i], my_list[i]
    
    def no_adjacent(my_list):
        my_list.sort()
        length = len(my_list)
        odd_ind = length%2
        odd_half = (length - odd_ind)/2
        for i in range(odd_half)[::2]:
            my_list[i], my_list[odd_half+odd_ind+i] = my_list[odd_half+odd_ind+i], my_list[i]
    
        #this is just for the limit case where the abundance of the most frequent is half of the list length
        if max([my_list.count(val) for val in set(my_list)]) + 1 - odd_ind > odd_half:
            max_val = my_list[0]
            max_count = my_list.count(max_val)
            for val in set(my_list):
                if my_list.count(val) > max_count:
                   max_val = val
                   max_count = my_list.count(max_val)
            while max_val in my_list:
                my_list.remove(max_val)
            out = [max_val]
            max_count -= 1
            for val in my_list:
                out.append(val)
                if max_count:
                    out.append(max_val)
                    max_count -= 1
            if max_count:
                print 'this is not working'
                return my_list
                #raise Exception('not possible')
            return out
        else:
            return my_list
    
    import collections
    import heapq
    
    
    class Sentinel:
        pass
    
    
    def david_eisenstat(lst):
        counts = collections.Counter(lst)
        heap = [(-count, key) for key, count in counts.items()]
        heapq.heapify(heap)
        output = []
        last = Sentinel()
        while heap:
            minuscount1, key1 = heapq.heappop(heap)
            if key1 != last or not heap:
                last = key1
                minuscount1 += 1
            else:
                minuscount2, key2 = heapq.heappop(heap)
                last = key2
                minuscount2 += 1
                if minuscount2 != 0:
                    heapq.heappush(heap, (minuscount2, key2))
            output.append(last)
            if minuscount1 != 0:
                heapq.heappush(heap, (minuscount1, key1))
        return output
    
    from collections import Counter
    from itertools import permutations
    from operator import itemgetter
    from random import randrange
    
    
    def get_mode(count):
        return max(count.items(), key=itemgetter(1))[0]
    
    
    def enum2(prefix, x, count, total, mode):
        prefix.append(x)
        count_x = count[x]
        if count_x == 1:
            del count[x]
        else:
            count[x] = count_x - 1
        yield from enum1(prefix, count, total - 1, mode)
        count[x] = count_x
        del prefix[-1]
    
    
    def enum1(prefix, count, total, mode):
        if total == 0:
            yield tuple(prefix)
            return
        if count[mode] * 2 - 1 >= total and [mode] != prefix[-1:]:
            yield from enum2(prefix, mode, count, total, mode)
        else:
            defect_okay = not prefix or count[prefix[-1]] * 2 > total
            mode = get_mode(count)
            for x in list(count.keys()):
                if defect_okay or [x] != prefix[-1:]:
                    yield from enum2(prefix, x, count, total, mode)
    
    
    def enum(seq):
        count = Counter(seq)
        if count:
            yield from enum1([], count, sum(count.values()), get_mode(count))
        else:
            yield ()
    
    
    def defects(lst):
        return sum(lst[i - 1] == lst[i] for i in range(1, len(lst)))
    
    
    def test(lst):
        perms = set(permutations(lst))
        opt = min(map(defects, perms))
        slow = {perm for perm in perms if defects(perm) == opt}
        fast = set(enum(lst))
        print(lst, fast, slow)
        assert slow == fast
    
    
    for r in range(10000):
        test([randrange(3) for i in range(randrange(6))])
    
    import collections, heapq
    def nonadjacent(keys):
        heap = [(-count, key) for key, count in collections.Counter(a).items()]
        heapq.heapify(heap)
        count, key = 0, None
        while heap:
            count, key = heapq.heapreplace(heap, (count, key)) if count else heapq.heappop(heap)
            yield key
            count += 1
        for index in xrange(-count):
            yield key
    
    >>> a = [1,2,3,3,2,2,1]
    >>> list(nonadjacent(a))
    [2, 1, 2, 3, 1, 2, 3]
    
    from collections import Counter
    from heapq import heapify, heappop, heapreplace
    from itertools import repeat
    
    def srgerg(data):
        heap = [(-freq+1, value) for value, freq in Counter(data).items()]
        heapify(heap)
    
        freq = 0
        while heap:
            freq, val = heapreplace(heap, (freq+1, val)) if freq else heappop(heap)
            yield val
        yield from repeat(val, -freq)
    
    def srgergpy2(data):
        heap = [(-freq+1, value) for value, freq in Counter(data).items()]
        heapify(heap)
    
        freq = 0
        result = list()
        while heap:
            freq, val = heapreplace(heap, (freq+1, val)) if freq else heappop(heap)
            result.append(val)
        result.extend(repeat(val, -freq))
        return result
    
    from heapq import heapify, heappop
    def distribute(values):
        counts = defaultdict(int)
        for value in values:
            counts[value] += 1
        counts = [(-count, key) for key, count in counts.iteritems()]
        heapify(counts)
        index = 0
        length = len(values)
        distributed = [None] * length
        while counts:
            count, value = heappop(counts)
            for _ in xrange(-count):
                distributed[index] = value
                index = index + 2 if index + 2 < length else 1
        return distributed
    
    EXAMPLE:
    set = abcaba
    firsts = abc
    repeats = aab
    
    perm3  set    select perm4  set    select perm5  set    select perm6
    
    abc    aab    a      abac   ab     b      ababc  a      a      ababac  
                                                                   ababca  
                                              abacb  a      a      abacab  
                                                                   abacba  
                         abca   ab     b      abcba  a      -
                                              abcab  a      a      abcaba  
    acb    aab    a      acab   ab     a      acaba  b      b      acabab  
                         acba   ab     b      acbab  a      a      acbaba  
    bac    aab    b      babc   aa     a      babac  a      a      babaca  
                                              babca  a      -
                         bacb   aa     a      bacab  a      a      bacaba  
                                              bacba  a      -  
    bca    aab    -
    cab    aab    a      caba   ab     b      cabab  a      a      cababa  
    cba    aab    -