在Python中生成循环移位/缩小拉丁方

问题描述 投票:8回答:7

只是想知道在Python中生成列表的所有循环移位的最有效方法是什么。在任何一个方向。例如,给定一个列表[1, 2, 3, 4],我想生成:

[[1, 2, 3, 4],
 [4, 1, 2, 3],
 [3, 4, 1, 2],
 [2, 3, 4, 1]]

通过将最后一个元素移动到前面来生成下一个排列,或者:

[[1, 2, 3, 4],
 [2, 3, 4, 1],
 [3, 4, 1, 2],
 [4, 1, 2, 3]]

通过将第一个元素移动到后面来生成下一个排列。

第二种情况对我来说稍微有些意思,因为它导致拉丁方形减少(第一种情况也给出拉丁方,只是没有减少),这正是我试图用来做实验块设计的。它实际上与第一种情况没有什么不同,因为它们只是彼此重新排序,但秩序仍然很重要。

我对第一种情况的当前实施是:

def gen_latin_square(mylist):
    tmplist = mylist[:]
    latin_square = []
    for i in range(len(mylist)):
        latin_square.append(tmplist[:])
        tmplist = [tmplist.pop()] + tmplist
    return latin_square

对于第二种情况,它:

def gen_latin_square(mylist):
    tmplist = mylist[:]
    latin_square = []
    for i in range(len(mylist)):
        latin_square.append(tmplist[:])
        tmplist = tmplist[1:] + [tmplist[0]]
    return latin_square

第一种情况似乎对我来说应该是合理有效的,因为它使用pop(),但你不能在第二种情况下这样做,所以我想听听有关如何更有效地做到这一点的想法。也许在itertools有什么东西会有帮助吗?或者第二种情况可能是一个双端队列?

python algorithm list permutation
7个回答
7
投票

对于第一部分,最简洁的方法可能是

a = [1, 2, 3, 4]
n = len(a)
[[a[i - j] for i in range(n)] for j in range(n)]
# [[1, 2, 3, 4], [4, 1, 2, 3], [3, 4, 1, 2], [2, 3, 4, 1]]

而对于第二部分

[[a[i - j] for i in range(n)] for j in range(n, 0, -1)]
# [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]

这些也应该比你的代码更有效,尽管我没有做任何时间安排。


16
投票

您可以使用collections.deque:

from collections import deque

g = deque([1, 2, 3, 4])

for i in range(len(g)):
    print list(g) #or do anything with permutation
    g.rotate(1) #for right rotation
    #or g.rotate(-1) for left rotation

它打印:

 [1, 2, 3, 4]
 [4, 1, 2, 3]
 [3, 4, 1, 2]
 [2, 3, 4, 1]

要将其更改为左旋转,只需将g.rotate(1)替换为g.rotate(-1)


3
投票

切片“保护法”a = a[:i] + a[i:]的变化

ns = list(range(5))
ns
Out[34]: [0, 1, 2, 3, 4]

[ns[i:] + ns[:i] for i in range(len(ns))]
Out[36]: 
[[0, 1, 2, 3, 4],
 [1, 2, 3, 4, 0],
 [2, 3, 4, 0, 1],
 [3, 4, 0, 1, 2],
 [4, 0, 1, 2, 3]]


[ns[-i:] + ns[:-i] for i in range(len(ns))]
Out[38]: 
[[0, 1, 2, 3, 4],
 [4, 0, 1, 2, 3],
 [3, 4, 0, 1, 2],
 [2, 3, 4, 0, 1],
 [1, 2, 3, 4, 0]]

1
投票

more_itertools是第三方库,为cyclic permutations提供工具:

import more_itertools as mit


mit.circular_shifts(range(1, 5))
# [(1, 2, 3, 4), (2, 3, 4, 1), (3, 4, 1, 2), (4, 1, 2, 3)]

另见Wikipedia

循环移位是一种特殊的循环置换,它又是一种特殊的置换。


0
投票

使用itertools来避免索引:

x = itertools.cycle(a)
[[x.next() for i in a] for j in a]

0
投票

@Bruno Lenzi的答案似乎不起作用:

In [10]: from itertools import cycle

In [11]: x = cycle('ABCD')

In [12]: print [[x.next() for _ in range(4)] for _ in range(4)]
[['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D']]

我在下面给出了一个正确的版本,但@ f5r5e5d的解决方案更快。

In [45]: def use_cycle(a):
    x=cycle(a)
    for _ in a:
        x.next()
        print [x.next() for _ in a]
   ....:         

In [46]: use_cycle([1,2,3,4])
[2, 3, 4, 1]
[3, 4, 1, 2]
[4, 1, 2, 3]
[1, 2, 3, 4]

In [50]: def use_slice(a):
    print [ a[n:] + a[:n] for n in range(len(a)) ]
  ....:     

In [51]: use_slice([1,2,3,4])
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]

In [54]: timeit.timeit('use_cycle([1,2,3,4])','from __main__ import use_cycle',number=100000)
Out[54]: 0.4884989261627197

In [55]: timeit.timeit('use_slice([1,2,3,4])','from __main__ import use_slice',number=100000)
Out[55]: 0.3103291988372803

In [58]: timeit.timeit('use_cycle([1,2,3,4]*100)','from __main__ import use_cycle',number=100)
Out[58]: 2.4427831172943115

In [59]: timeit.timeit('use_slice([1,2,3,4]*100)','from __main__ import use_slice',number=100)
Out[59]: 0.12029695510864258

为了计时目的,我删除了use_cycleuse_slice中的print语句。


0
投票

这将是我的解决方案。

#given list
a = [1,2,3,4]
#looping through list
for i in xrange(len(a)):
    #inserting last element at the starting
    a.insert(0,a[len(a)-1])
    #removing the last element
    a = a[:len(a)-1]
    #printing if you want to
    print a

这将输出以下内容:

[4, 1, 2, 3]
[3, 4, 1, 2]
[2, 3, 4, 1]
[1, 2, 3, 4]

您也可以使用pop而不是使用列表切片,但pop的问题是它会返回一些东西。

此外,上述代码适用于任何长度的列表。我没有检查代码的性能。我假设它会更好。

你应该看一下Python docs,以便更好地理解List切片。

© www.soinside.com 2019 - 2024. All rights reserved.