2013-03-14 8 views
6

Cách hiệu quả nhất (theo thời gian) là kiểm tra xem hai danh sách tương đối ngắn (khoảng 3-8 phần tử) có được sao chép của nhau không? Và nếu có, xác định và trả lại khoản bù trừ?Trong Python, xác định hiệu quả nếu hai danh sách được dịch chuyển các bản sao của nhau

Đây là đoạn mã ví dụ và đầu ra tôi muốn:

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

Chức năng có thể có mục trùng lặp. Nếu nhiều hơn một offset là hợp lệ, trả về bất kỳ offset nào.

+2

nếu danh sách là quần short, tại sao phải quan tâm đến hiệu quả thời gian! –

+1

Vâng, nếu tốc độ quan trọng, bạn nên sử dụng cấu trúc dữ liệu thay thế. –

Trả lời

4

Tìm kiếm hai bản sao của danh sách đầu tiên cho phép chúng tôi để tránh thực hiện nối quá mức:

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) 
+0

3 dòng, không có câu lệnh thử, hiệu suất rất gần với các giải pháp tốt nhất khác. Cảm ơn! – devtk

+0

lưu ý: đó là thời gian 'O (n ** 2)', 'O (n)'. Nếu 'n' lớn; [Giải pháp @thkang] (http://stackoverflow.com/a/15415525/4279) là thời gian O (n), không gian O (1) có thể thích hợp hơn. Mặc dù cho 'n ~ 3..8' nó không quan trọng. – jfs

5

đây là một phiên bản lặp đơn giản mà không được công việc trong lặp 2n (n là chiều dài của danh sách)

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 

và từ đặc điểm kỹ thuật của bạn,

không nên is_shifted_copy([1, 1, 2], [2, 1, 1]) trở 2?

+0

Tôi nghĩ rằng thông số kỹ thuật là nhất quán. Nhưng đó là một chi tiết. Tôi không thực sự quan tâm nếu bạn quay trở lại 1 hoặc 2, miễn là nó nhất quán. – devtk

3

Đây là một giải pháp dựa trên các chỉ mục và cắt:

>>> def is_shifted_copy(l1, l2): 
    try: 
     return [l1[-i:] + l1[:-i] for i in range(len(l1))].index(l2) 
    except ValueError: 
     return None 

Kết quả được như mong đợi:

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

Dưới đây là một phiên bản sửa đổi của giải pháp NPE, mà kiểm tra cho tất cả các vị trí phù hợp với khả năng . Nó so sánh thuận lợi để (và ecatmur của) các giải pháp iterator thông minh thkang của:

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 

Lưu ý: Tôi đã thay đổi giá trị trả về trong is_shifted_copy_thkangis_shifted_copy_ecatmur để tất cả ba phiên bản sẽ vượt qua các bài kiểm tra trong bài đăng gốc.

Tôi đã đánh giá các hàm có và không có thay đổi và thấy nó không ảnh hưởng đáng kể đến hiệu suất của các hàm.

Ví dụ, với 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 

Với 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 
+0

tại sao 'i% n' nếu' i trong phạm vi (n) '? Chỉ là 'tôi' là đủ. – jfs

+0

@ J.F.Sebastian: Cảm ơn bạn đã phát hiện lỗi. Tôi đã dự định nó là '-i% n cho tôi trong phạm vi (n) ...' (vì vậy tất cả các phiên bản sẽ vượt qua các bài kiểm tra tương tự) nhưng bằng cách nào đó có một lỗi đánh máy. – unutbu

+0

bạn có thể sử dụng 'next_item = iter (list2) .next' trong phiên bản @ thkang's. Không nên trả về '0' trong mệnh đề' else' (trường hợp danh sách trống)? – jfs