比较python中的旋转列表

时间:2013-04-02 20:27:57

标签: python readability

我正在尝试比较两个列表以确定一个是否是另一个列表的旋转(循环置换),例如:

a = [1, 2, 3]
b = [1, 2, 3] or [2, 3, 1] or [3, 1, 2]

是所有比赛,而:

b = [3, 2, 1] is not

要做到这一点,我有以下代码:

def _matching_lists(a, b):
    return not [i for i, j in zip(a,b) if i != j]

def _compare_rotated_lists(a, b):
    rotations = [b[i:] + b[:i] for i in range(len(b))]
    matches = [i for i in range(len(rotations)) if _matching_lists(a, rotations[i])]
    return matches

这将构建b的所有可能旋转的列表,然后比较每个旋转。是否可以在不构建中间列表的情况下执行此操作?性能并不重要,因为列表通常只有四个项目。我主要关心的是代码的清晰度。

列表的长度始终相同。

最佳答案(保持匹配轮换列表)似乎是:

def _compare_rotated_lists(a, b):
    return [i for i in range(len(b)) if a == b[i:] + b[:i]]

4 个答案:

答案 0 :(得分:7)

您不需要_matching_lists功能,因为您可以使用==

>>> [1,2,3] == [1,2,3]
True
>>> [1,2,3] == [3,1,2]
False

我建议在找到匹配后立即使用any()返回,并使用生成器表达式来避免在内存中构建旋转列表:

def _compare_rotated_lists(a, b):
    """Return `True` if the list `a` is equal to a rotation of the list `b`."""
    return any(a == b[i:] + b[:i] for i in range(len(b)))

您可以考虑检查列表的长度是否相同,以便快速拒绝简单的案例。

    return len(a) == len(b) and any(a == b[i:] + b[:i] for i in range(len(b)))

正如评论中所讨论的,如果您知道ab的元素是可清除的,则可以使用collections.Counter进行初始比较:

    return Counter(a) == Counter(b) and any(a == b[i:] + b[:i] for i in range(len(b)))

如果您知道ab的元素具有可比性,则可以使用sorted进行初始比较:

    return sorted(a) == sorted(b) and any(a == b[i:] + b[:i] for i in range(len(b)))

答案 1 :(得分:2)

如果我理解正确,您想要查找ba的排列,而不是a的排列?这是一个非常简单,可读和通用的解决方案:

>>> from itertools import permutations 
>>> a = (1, 2, 3)
>>> b = (3, 1, 2)
>>> c = (3, 2, 1)
>>> results = set(permutations(a)) - set((a, tuple(sorted(a, reverse=True))))
>>> b in results
True
>>> c in results
False

答案 2 :(得分:1)

怎么样:

def canon(seq):
    n = seq.index(min(seq))
    return seq[n:] + seq[:n]

def is_rotation(a, b):
    return canon(a) == canon(b)

print is_rotation('abcd', 'cdab') # True
print is_rotation('abcd', 'cdba') # False

无需生成所有轮换,只是为了确定两个列表是否相互轮换。

答案 3 :(得分:0)

我用一些例子测试了这段代码,但效果很好。

def compare(a,b):
firstInA = a[0]
firstInB = b.index(firstInA)
secondInA = a[1]
secondInB = b.index(secondInA)
if (secondInB == firstInB + 1) or (secondInB == 0 and firstInB == 2):
    return True
else:
    return False

我试过了:

a = [1,2,3]
b = [1,2,3]
print(compare(a,b))
c = [1,2,3]
d = [3,1,2]
print(compare(c,d))
e = [1,2,3]
f = [3,2,1]
print(compare(e,f))

他们返回TrueTrueFalse 这仅适用于大小为3的列表。如果您需要更多,请在if语句中添加thirdInA和thirdInB,并且您将始终需要一个小于列表长度的列表,因为如果您知道全部但是有一个到位,那么最后只剩下现场了。