【问题标题】:Generating circular shifts / reduced Latin Squares in Python在 Python 中生成循环移位/减少拉丁方
【发布时间】:2011-07-15 21:28:18
【问题描述】:

只是想知道在 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 中的某些内容会有所帮助?或者可能是第二种情况的双端队列?

【问题讨论】:

  • 您的实现不起作用 - 他们正在添加列表(整数)和列表,这是不可能的。此外,它们在每次迭代中都附加相同的列表实例,因此您最终会得到一个由 n 次同一行组成的正方形。
  • 哎呀,错过了,谢谢。没有实际测试代码:p

标签: python algorithm list permutation


【解决方案1】:

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

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]]

这些也应该比你的代码更有效率,虽然我没有做任何计时。

【讨论】:

  • 哇,没想到你可以使用列表推导来做到这一点!也许我应该更努力地思考。不必使用任何额外模块的道具。
【解决方案2】:

你可以使用 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)

【讨论】:

  • rotate 方法很酷。从来不知道出队可以做到这一点。话又说回来,我可能应该在问之前仔细阅读文档。
  • 由于是双端队列,rotate操作大概是高效实现的。
  • 文档确实是我们最好的朋友。 :)
【解决方案3】:

使用 itertools 避免索引:

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

【讨论】:

    【解决方案4】:

    这将是我的解决方案。

    #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 以更好地了解列表切片。

    【讨论】:

      【解决方案5】:

      切片“守恒定律”的变体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]]
      

      【讨论】:

        【解决方案6】:

        @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 中的打印语句。

        【讨论】:

          【解决方案7】:

          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

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

          【讨论】:

            猜你喜欢
            • 2012-07-16
            • 1970-01-01
            • 2015-11-01
            • 1970-01-01
            • 2015-08-18
            • 1970-01-01
            • 2019-01-26
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多