我有一个包含重复元素的列表,即orig = [1,1,1,2,2,3]
。
我想创建一个derangement b = f(orig)
,以使b
中的每个位置值都与orig
中的值不同:
b[i] != orig[i], for all i
当orig
中的所有元素都是唯一的时,我知道一种解决方案,但这是更困难的情况。
使用python开发解决方案,但是任何语言都可以。
答案 0 :(得分:1)
效率不高的解决方案显然是
import itertools
set([s for s in itertools.permutations(orig) if not any([a == b for a, b in zip(s, orig)])])
第二种方法和第一种改进是使用this perm_unique
:
[s for s in perm_unique(orig) if not any([a == b for a, b in zip(s, orig)])]
第三种方法是使用此超级快速unique_permutations
algorithm。
import copy
[copy.copy(s) for s in unique_permutations(orig) if not any([a == b for a, b in zip(s, orig)])]
在装有%%timeit
的笔记本中,初始方法为841 µs
,然后我们改进为266 µs
,然后改进为137 µs
。
修改
不能停止考虑,对第二种方法进行了少量编辑。没有时间去研究最后一种方法。有关说明,请先参阅原始帖子(上面的链接)。然后,我仅添加了强制执行混乱状态的检查and el != elements[depth]
。这样,我们得出的运行时间为50 µs
。
from collections import Counter
def derangement_unique(elements):
list_unique = Counter(elements)
length_list = len(elements) # will become depth in the next function
placeholder = [0]*length_list # will contain the result
return derangement_unique_helper(elements, list_unique, placeholder, length_list-1)
def derangement_unique_helper(elements, list_unique, result_list, depth):
if depth < 0: # arrived at a solution
yield tuple(result_list)
else:
# consider all elements and how many times they should still occur
for el, count in list_unique.items():
# ... still required and not breaking the derangement requirement
if count > 0 and el != elements[depth]:
result_list[depth] = el # assignment element
list_unique[el] -= 1 # substract number needed
# loop for all possible continuations
for g in derangement_unique_helper(elements, list_unique, result_list, depth-1):
yield g
list_unique[el] += 1
list(derangement_unique(orig))
答案 1 :(得分:0)
如果您的列表中包含大量重复项,则可能很难快速找到乱序。
在这种情况下,您可以尝试图形方法。
处理初始列表以创建一个图形,其中每个项目都与不相等的元素相连(易于排序的列表)。
然后构建完美匹配(如果元素数量为偶数)或接近完美的匹配(对于奇数,这里您需要找到一些合适的对并将单个节点加入其中)。
匹配的边缘表示交换以进行重新排列。
Python库networkx
应该包含所需的方法。