检查两个相对较短(约3-8个元素)列表是否是彼此的移位副本的最有效(及时)方式是什么?如果是这样,确定并返回偏移量?
这是示例代码和输出我喜欢:
>>> def is_shifted_copy(list_one, list_two):
>>> # TODO
>>>
>>> is_shifted_copy([1, 2, 3], [1, 2, 3])
0
>>> is_shifted_copy([1, 2, 3], [3, 1, 2])
1
>>> is_shifted_copy([1, 2, 3], [2, 3, 1])
2
>>> is_shifted_copy([1, 2, 3], [3, 2, 1])
None
>>> is_shifted_copy([1, 2, 3], [1])
None
>>> is_shifted_copy([1, 1, 2], [2, 1, 1])
1
列表可能包含重复的条目。如果有多个偏移量有效,则返回任何偏移量。
答案 0 :(得分:5)
这是一个简单的迭代器版本,它在2n次迭代中完成工作(n是列表的长度)
import itertools
def is_shifted_copy(list1, list2):
if len(list1) != len(list2):
return False
iterator = iter(list2)
for i, item in enumerate(itertools.chain(list1, list1)):
try:
if item == iterator.next():
continue
else:
iterator = iter(list2)
except StopIteration:
return i - len(list2)
else:
return False
print is_shifted_copy([1, 2, 3], [1, 2, 3]) #0
print is_shifted_copy([1, 2, 3], [3, 1, 2]) #2
print is_shifted_copy([1, 2, 3], [3, 2, 1]) #False
print is_shifted_copy([1, 2, 3], [2, 3, 1]) #1
print is_shifted_copy([1, 1, 2], [2, 1, 1]) #2
print is_shifted_copy([1, 2, 3], [1]) #False
print is_shifted_copy([1, 2, 1], [2, 1, 1]) #1
print is_shifted_copy([1, 1, 1], [1, 1, 1]) #0
并根据您的规范
不应is_shifted_copy([1, 1, 2], [2, 1, 1])
返回2
?
答案 1 :(得分:4)
搜索第一个列表的两个副本可以避免执行过多的连接:
def is_shifted_copy(l1, l2):
l1l1 = l1 * 2
n = len(l1)
return next((i for i in range(n) if l1l1[i:i + n] == l2), None)
答案 2 :(得分:3)
这是一个基于索引和切片的解决方案:
>>> def is_shifted_copy(l1, l2):
try:
return [l1[-i:] + l1[:-i] for i in range(len(l1))].index(l2)
except ValueError:
return None
结果如预期:
>>> is_shifted_copy([1, 2, 3], [1, 2, 3])
0
>>> is_shifted_copy([1, 2, 3], [3, 1, 2])
1
>>> is_shifted_copy([1, 2, 3], [2, 3, 1])
2
>>> is_shifted_copy([1, 2, 3], [2, 1, 3])
None
答案 3 :(得分:3)
以下是NPE解决方案的修改版本,它会检查所有可能的匹配位置。它与thkang(和ecatmur的)聪明的迭代器解决方案相比毫不逊色:
import itertools as IT
def is_shifted_copy_thkang(list1, list2):
N = len(list1)
if N != len(list2):
return None
elif N == 0:
return 0
next_item = iter(list2).next
for i, item in enumerate(IT.chain(list1, list1)):
try:
if item == next_item():
continue
else:
next_item = iter(list2).next
except StopIteration:
return -i % N
else:
return None
def is_shifted_copy_NPE(list1, list2):
N = len(list1)
if N != len(list2):
return None
elif N == 0:
return 0
pos = -1
first = list1[0]
while pos < N:
try:
pos = list2.index(first, pos+1)
except ValueError:
break
if (list2 + list2)[pos:pos+N] == list1:
return pos
return None
def is_shifted_copy_ecatmur(l1, l2):
l1l1 = l1 * 2
n = len(l1)
return next((-i % n for i in range(n) if l1l1[i:i + n] == l2), None)
tests = [
# ([], [], 0),
([1, 2, 3], [1, 2, 3], 0),
([1, 2, 3], [3, 1, 2], 1),
([1, 2, 3], [2, 3, 1], 2),
([1, 2, 3], [3, 2, 1], None),
([1, 2, 3], [1], None),
([1, 1, 2], [2, 1, 1], 1),
([1,2,3,1,3,2], [1,3,2,1,2,3], 3)
]
for list1, list2, answer in tests:
print(list1, list2, answer)
assert is_shifted_copy_thkang(list1, list2) == answer
assert is_shifted_copy_NPE(list1, list2) == answer
assert is_shifted_copy_ecatmur(list1, list2) == answer
In [378]: %timeit is_shifted_copy_thkang([1, 2, 3], [3, 1, 2])
100000 loops, best of 3: 3.5 us per loop
In [377]: %timeit is_shifted_copy_ecatmur([1, 2, 3], [3, 1, 2])
100000 loops, best of 3: 2.37 us per loop
In [379]: %timeit is_shifted_copy_NPE([1, 2, 3], [3, 1, 2])
1000000 loops, best of 3: 1.13 us per loop
注意:我更改了is_shifted_copy_thkang
和is_shifted_copy_ecatmur
中的返回值,以便所有三个版本都会通过原始帖子中的测试。
我使用和不使用更改对函数进行基准测试,发现它不会显着影响函数的性能。
例如,使用return i
:
In [384]: %timeit is_shifted_copy_thkang([1, 2, 3], [3, 1, 2])
100000 loops, best of 3: 3.38 us per loop
使用return -i % N
:
In [378]: %timeit is_shifted_copy_thkang([1, 2, 3], [3, 1, 2])
100000 loops, best of 3: 3.5 us per loop