有效地生成长度为 N 的列表中长度为 K 的循环移位的所有排列
Generate all permutations of cyclic shift of length K in a list of length N efficiently
How can I generate all permutations of cyclic shifts of length k in a list of length n. Here shift is cyclic and right. Notice that:
如果 K==1 ,则没有移位。因此,这些 0 班次没有排列。
如果 K==2 ,这相当于交换元素。因此所有n!可以生成排列。
例如。如果列表是 [1 4 2],K=2(因此从 0 到 N-K,循环)
P1: [1,4,2] #Original list. No shift.
P2: [4,1,2] #Shift from 0 of [1,4,2]
P3: [4,2,1] #Shift from 1 of [4,1,2] as 0 gives P1
P4: [2,4,1] #Shift from 0 of [4,2,1]
P5: [2,1,4] #Shift from 1 of [1,4,2] as 0 of P4=P3
P6: [1,2,4] #Shift from 0 of [2,1,4]
如果 K==3,事情会变得有趣,因为一些排列被排除在外。
例如。 if list=[1,3,4,2],K=3(因此从索引 0 到 4-3,循环)
P1 : [1,3,4,2] #Original list. No shift.
P2 : [4,1,3,2] #Shift from 0th of [1,3,4,2]
P3 : [3,4,1,2] #Shift from 0th of [4,1,3,2]
P4 : [3,2,4,1] #Shift from 1th of [3,4,1,2] as 0th gives P1
P5 : [4,3,2,1] #Shift from 0th of [3,2,4,1]
P6 : [2,4,3,1] #Shift from 0th of [4,3,2,1]
P7 : [2,1,4,3] #Shift from 1th of [2,4,3,1] as 0th gives P3
P8 : [4,2,1,3] #Shift from 0th of [2,1,4,3]
P9 : [1,4,2,3] #Shift from 0th of [4,2,1,3]
P10: [2,3,1,4] #Shift from 1th of [2,1,4,3] as 0 from P9=P7,1 from P9=P1,1 from P8=P5
P11: [1,2,3,4] #Shift from 0th of [2,3,1,4]
P12: [3,1,2,4] #Shift from 0th of [1,2,3,4]
#Now,all have been generated, as moving further will lead to previously found values.
Notice,that these permutations are half (12) of what should've been (24).
To, implement this, algorithm, I am currently using backtracking. Here's what I have tried so far (in Python)
def get_possible_cyclic(P,N,K,stored_perms): #P is the original list
from collections import deque
if P in stored_perms:
return #Backtracking to the previous
stored_perms.append(P)
for start in xrange(N-K+1):
"""
Shifts cannot wrap around. Eg. 1,2,3,4 ,K=3
Recur for (1,2,3),4 or 1,(2,3,4) where () denotes the cycle
"""
l0=P[:start] #Get all elements that are before cycle ranges
l1=deque(P[start:K+start]) #Get the elements we want in cycle
l1.rotate() #Form their cycle
l2=P[K+start:] #Get all elements after cycle ranges
l=l0+list(l1)+l2 #Form the required list
get_possible_cyclic(l,N,K,stored_perms)
for index,i in enumerate(stored_perms):
print i,index+1
get_possible_cyclic([1,3,4,2],4,3,[])
get_possible_cyclic([1,4,2],3,2,[])
This produces the output
[1, 3, 4, 2] 1
[4, 1, 3, 2] 2
[3, 4, 1, 2] 3
[3, 2, 4, 1] 4
[4, 3, 2, 1] 5
[2, 4, 3, 1] 6
[2, 1, 4, 3] 7
[4, 2, 1, 3] 8
[1, 4, 2, 3] 9
[2, 3, 1, 4] 10
[1, 2, 3, 4] 11
[3, 1 ,2, 4] 12
[1, 4, 2] 1
[4, 1, 2] 2
[4, 2, 1] 3
[2, 4, 1] 4
[2, 1, 4] 5
[1, 2, 4] 6
This is exactly what I want, but a lot lot slower,since here the recursion depth exceeds for N>7. I hope, I have explained myself clearly. Anyone, with any optimizations?
支票
if P in stored_perms:
随着 stored_perms
的增长越来越慢,因为它需要一次将 P
与 stored_perms
的元素进行比较,直到找到副本或结束遇到列表。由于每个排列都将添加到 stored_perms
一次,因此与 P
的比较次数至少是找到的排列数的二次方,通常是所有可能的排列或其中的一半,具体取决于关于 k 是偶数还是奇数(假设 1 < k < N)。
使用 set 效率更高。 Python 的集合基于散列-table,因此成员资格检查通常是 O(1) 而不是 O(N)。但是,有一些限制:
添加到集合中的元素需要是 hashable,并且 Python 列表不可散列。幸运的是,元组是可散列的,所以一个小的改变就解决了这个问题。
迭代集合是不可预测的table。特别是,您无法在迭代时可靠地修改集合。
除了将 P 更改为元组和 stored_perms 更改为集合之外,值得考虑基于工作队列而不是递归搜索的搜索。我不知道它是否会更快,但它避免了递归深度的任何问题。
将所有这些放在一起,我将以下内容放在一起:
def get_cyclics(p, k):
found = set() # set of tuples we have seen so far
todo = [tuple(p)] # list of tuples we still need to explore
n = len(p)
while todo:
x = todo.pop()
for i in range(n - k + 1):
perm = ( x[:i] # Prefix
+ x[i+1:i+k] + x[i:i+1] # Rotated middle
+ x[i+k:] # Suffix
)
if perm not in found:
found.add(perm)
todo.append(perm)
for x in found:
print(x)
How can I generate all permutations of cyclic shifts of length k in a list of length n. Here shift is cyclic and right. Notice that:
如果 K==1 ,则没有移位。因此,这些 0 班次没有排列。
如果 K==2 ,这相当于交换元素。因此所有n!可以生成排列。
例如。如果列表是 [1 4 2],K=2(因此从 0 到 N-K,循环)
P1: [1,4,2] #Original list. No shift.
P2: [4,1,2] #Shift from 0 of [1,4,2]
P3: [4,2,1] #Shift from 1 of [4,1,2] as 0 gives P1
P4: [2,4,1] #Shift from 0 of [4,2,1]
P5: [2,1,4] #Shift from 1 of [1,4,2] as 0 of P4=P3
P6: [1,2,4] #Shift from 0 of [2,1,4]
如果 K==3,事情会变得有趣,因为一些排列被排除在外。
例如。 if list=[1,3,4,2],K=3(因此从索引 0 到 4-3,循环)
P1 : [1,3,4,2] #Original list. No shift.
P2 : [4,1,3,2] #Shift from 0th of [1,3,4,2]
P3 : [3,4,1,2] #Shift from 0th of [4,1,3,2]
P4 : [3,2,4,1] #Shift from 1th of [3,4,1,2] as 0th gives P1
P5 : [4,3,2,1] #Shift from 0th of [3,2,4,1]
P6 : [2,4,3,1] #Shift from 0th of [4,3,2,1]
P7 : [2,1,4,3] #Shift from 1th of [2,4,3,1] as 0th gives P3
P8 : [4,2,1,3] #Shift from 0th of [2,1,4,3]
P9 : [1,4,2,3] #Shift from 0th of [4,2,1,3]
P10: [2,3,1,4] #Shift from 1th of [2,1,4,3] as 0 from P9=P7,1 from P9=P1,1 from P8=P5
P11: [1,2,3,4] #Shift from 0th of [2,3,1,4]
P12: [3,1,2,4] #Shift from 0th of [1,2,3,4]
#Now,all have been generated, as moving further will lead to previously found values.
Notice,that these permutations are half (12) of what should've been (24). To, implement this, algorithm, I am currently using backtracking. Here's what I have tried so far (in Python)
def get_possible_cyclic(P,N,K,stored_perms): #P is the original list
from collections import deque
if P in stored_perms:
return #Backtracking to the previous
stored_perms.append(P)
for start in xrange(N-K+1):
"""
Shifts cannot wrap around. Eg. 1,2,3,4 ,K=3
Recur for (1,2,3),4 or 1,(2,3,4) where () denotes the cycle
"""
l0=P[:start] #Get all elements that are before cycle ranges
l1=deque(P[start:K+start]) #Get the elements we want in cycle
l1.rotate() #Form their cycle
l2=P[K+start:] #Get all elements after cycle ranges
l=l0+list(l1)+l2 #Form the required list
get_possible_cyclic(l,N,K,stored_perms)
for index,i in enumerate(stored_perms):
print i,index+1
get_possible_cyclic([1,3,4,2],4,3,[])
get_possible_cyclic([1,4,2],3,2,[])
This produces the output
[1, 3, 4, 2] 1
[4, 1, 3, 2] 2
[3, 4, 1, 2] 3
[3, 2, 4, 1] 4
[4, 3, 2, 1] 5
[2, 4, 3, 1] 6
[2, 1, 4, 3] 7
[4, 2, 1, 3] 8
[1, 4, 2, 3] 9
[2, 3, 1, 4] 10
[1, 2, 3, 4] 11
[3, 1 ,2, 4] 12
[1, 4, 2] 1
[4, 1, 2] 2
[4, 2, 1] 3
[2, 4, 1] 4
[2, 1, 4] 5
[1, 2, 4] 6
This is exactly what I want, but a lot lot slower,since here the recursion depth exceeds for N>7. I hope, I have explained myself clearly. Anyone, with any optimizations?
支票
if P in stored_perms:
随着 stored_perms
的增长越来越慢,因为它需要一次将 P
与 stored_perms
的元素进行比较,直到找到副本或结束遇到列表。由于每个排列都将添加到 stored_perms
一次,因此与 P
的比较次数至少是找到的排列数的二次方,通常是所有可能的排列或其中的一半,具体取决于关于 k 是偶数还是奇数(假设 1 < k < N)。
使用 set 效率更高。 Python 的集合基于散列-table,因此成员资格检查通常是 O(1) 而不是 O(N)。但是,有一些限制:
添加到集合中的元素需要是 hashable,并且 Python 列表不可散列。幸运的是,元组是可散列的,所以一个小的改变就解决了这个问题。
迭代集合是不可预测的table。特别是,您无法在迭代时可靠地修改集合。
除了将 P 更改为元组和 stored_perms 更改为集合之外,值得考虑基于工作队列而不是递归搜索的搜索。我不知道它是否会更快,但它避免了递归深度的任何问题。
将所有这些放在一起,我将以下内容放在一起:
def get_cyclics(p, k):
found = set() # set of tuples we have seen so far
todo = [tuple(p)] # list of tuples we still need to explore
n = len(p)
while todo:
x = todo.pop()
for i in range(n - k + 1):
perm = ( x[:i] # Prefix
+ x[i+1:i+k] + x[i:i+1] # Rotated middle
+ x[i+k:] # Suffix
)
if perm not in found:
found.add(perm)
todo.append(perm)
for x in found:
print(x)