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 -