2012-01-09 23 views
76

sẽ kiểm tra mã sau nếu xy là những giá trị riêng biệt (các biến x, y, z chỉ có thể có giá trị a, b, hoặc c) và nếu như vậy, đặt z với nhân vật thứ ba:Làm cách nào để tìm giá trị còn thiếu chính xác hơn?

if x == 'a' and y == 'b' or x == 'b' and y == 'a': 
    z = 'c' 
elif x == 'b' and y == 'c' or x == 'c' and y == 'b': 
    z = 'a' 
elif x == 'a' and y == 'c' or x == 'c' and y == 'a': 
    z = 'b' 

Có thể thực hiện điều này theo cách ngắn gọn, dễ đọc hơn và hiệu quả hơn không?

+6

Câu trả lời ngắn gọn là Các bộ của Python rất tuyệt vời để kiểm tra tính khác biệt và để tính toán các phần tử không sử dụng. –

+1

Cảm ơn tất cả các câu trả lời, tôi đoán tôi sẽ sử dụng giải pháp bằng cách sử dụng thiết lập như là hợp lý của nó nhanh chóng và dễ đọc, Bảng tra cứu dựa trên câu trả lời của Óscar López cũng intresting. –

Trả lời

62
z = (set(("a", "b", "c")) - set((x, y))).pop() 

Tôi giả định rằng một trong ba trường hợp trong mã của bạn nắm giữ. Trong trường hợp này, tập hợp set(("a", "b", "c")) - set((x, y)) sẽ bao gồm một phần tử duy nhất, được trả về bởi pop().

Edit: Theo đề nghị của Raymond hettinger trong các ý kiến, bạn cũng có thể sử dụng tuple giải nén để trích xuất các phần tử từ tập:

z, = set(("a", "b", "c")) - set((x, y)) 
+26

Nếu bạn đang sử dụng Python 2.7/3.1 hoặc mới hơn, bạn có thể viết nó chính xác hơn bằng cách sử dụng các chữ được đặt, như sau: 'z = ({'a', 'b', 'c'} - {x, y}) .pop() ' – Taymon

+7

* pop() * là không cần thiết và chậm. Thay vào đó, hãy sử dụng giải nén tuple. Ngoài ra, '' set (("a", "b", "c")) '' là bất biến nên nó có thể được precomputed một thời gian, chỉ để lại sự khác biệt thiết lập được sử dụng trong một vòng lặp (nếu nó không phải là được sử dụng trong một vòng lặp, sau đó chúng tôi không quan tâm nhiều về tốc độ). –

+0

OP Bunny làm cho nó rõ ràng, bằng cách "kiểm tra nếu giá trị khác biệt" và "nếu có", bạn không thể đếm trên x và y là khác nhau. –

18
z = (set('abc') - set(x + y)).pop() 

Dưới đây là tất cả các kịch bản để hiển thị rằng nó hoạt động:

>>> (set('abc') - set('ab')).pop() # x is a/b and y is b/a 
'c' 
>>> (set('abc') - set('bc')).pop() # x is b/c and y is c/b 
'a' 
>>> (set('abc') - set('ac')).pop() # x is a/c and y is c/a 
'b' 
2

tôi nghĩ rằng nó nên trông như thế:

z = (set(("a", "b", "c")) - set((x, y))).pop() if x != y else None 
+12

'len (bộ ((x, y))) == 2' là cách dễ đọc nhất để viết' x! = Y' mà tôi từng thấy :) –

+0

Vâng, Sven))) Cảm ơn về bình luận của bạn. Kịch bản này có một ý tưởng cơ bản khác khi tôi bắt đầu viết nó)) Cuối cùng tôi đã quên chỉnh sửa nó. – selfnamed

13

Tôi nghĩ rằng giải pháp của Sven Marnach và F.J là đẹp, nhưng nó không nhanh hơn trong bài kiểm tra nhỏ của tôi. Đây là phiên bản được tối ưu hóa Raymond bằng cách sử dụng một tính toán trước set:

$ python -m timeit -s "choices = set('abc')" \ 
        -s "x = 'c'" \ 
        -s "y = 'a'" \ 
         "z, = choices - set(x + y)" 
1000000 loops, best of 3: 0.689 usec per loop 

Đây là giải pháp gốc:

$ python -m timeit -s "x = 'c'" \ 
        -s "y = 'a'" \ 
         "if x == 'a' and y == 'b' or x == 'b' and y == 'a':" \ 
         " z = 'c'" \ 
         "elif x == 'b' and y == 'c' or x == 'c' and y == 'b':" \ 
         " z = 'a'" \ 
         "elif x == 'a' and y == 'c' or x == 'c' and y == 'a':" \ 
         " z = 'b'" 
10000000 loops, best of 3: 0.310 usec per loop 

Lưu ý rằng đây là tồi tệ nhất có thể đầu vào cho if -statements từ tất cả sáu so sánh sẽ phải được thử. Thử nghiệm với tất cả các giá trị cho xy cho:

x = 'a', y = 'b': 0.084 usec per loop 
x = 'a', y = 'c': 0.254 usec per loop 
x = 'b', y = 'a': 0.133 usec per loop 
x = 'b', y = 'c': 0.186 usec per loop 
x = 'c', y = 'a': 0.310 usec per loop 
x = 'c', y = 'b': 0.204 usec per loop 

Các set biến thể dựa trên cho thấy việc thực hiện tương tự cho đầu vào khác nhau, nhưng nó là một cách nhất quán giữa chậm 2 và 8 lần. Lý do là phiên bản dựa trên if chạy mã đơn giản hơn nhiều: kiểm tra bình đẳng so với băm.

Tôi nghĩ cả hai loại giải pháp đều có giá trị: điều quan trọng là phải biết rằng việc tạo cấu trúc dữ liệu "phức tạp" như đặt chi phí cho bạn hiệu suất - trong khi chúng cung cấp cho bạn nhiều khả năng đọc và tốc độ phát triển . Các kiểu dữ liệu phức tạp cũng tốt hơn nhiều khi thay đổi mã: thật dễ dàng để mở rộng giải pháp dựa trên thiết lập thành bốn, năm, ... biến trong khi các câu lệnh if nhanh chóng biến thành cơn ác mộng bảo trì.

+1

@martinGeisler cảm ơn rất nhiều vì câu trả lời của bạn tôi không biết rằng chúng tôi có thể làm những việc như thế này trong python.I có cảm giác rằng giải pháp Chessmasters sẽ hoạt động tốt và hiệu quả, tôi sẽ cố gắng thử nghiệm như bạn đã làm với các câu trả lời khác và cho bạn biết. –

+0

đồng ý rằng khả năng đọc, nhưng tôi chỉ cố gắng giải quyết một thách thức lập trình có thể khiến tôi phải làm việc ở đâu đó, vì vậy bây giờ hiệu quả đầu tiên của nó là khả năng đọc thứ hai của tôi bây giờ: P –

+1

Giải pháp dựa trên thiết lập tối ưu * conciseness * và * readability * (và * sang trọng *). Nhưng * hiệu quả * cũng đã được đề cập, vì vậy tôi đã đi và điều tra hiệu suất của các giải pháp được đề xuất. –

8
z = 'a'*('a' not in x+y) or 'b'*('b' not in x+y) or 'c' 

hoặc ít hackish và sử dụng Chuyển nhượng có điều kiện

z = 'a' if ('a' not in x+y) else 'b' if ('b' not in x+y) else 'c' 

nhưng có lẽ là giải pháp dict là nhanh hơn ... bạn muốn có thời gian nó.

8

Hãy thử tùy chọn này, sử dụng các từ điển:

z = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'}[x+y] 

Tất nhiên, nếu phím x+y là không có mặt trong bản đồ, nó sẽ tạo ra một KeyError mà bạn sẽ phải xử lý.

Nếu từ điển được precomputed một lần duy nhất và được lưu trữ để sử dụng trong tương lai, truy cập sẽ nhanh hơn nhiều, vì không có cấu trúc dữ liệu mới sẽ được tạo ra cho mỗi đánh giá, chỉ cần một chuỗi nối và tra cứu từ điển:

lookup_table = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'} 
z = lookup_table[x+y] 
+2

Chỉ cần cho vui, đây là một lựa chọn dict: '{1: 'c', 2: 'b', 3: 'a'} [ord (x) + ord (y) -ord ('a') * 2 ] ', phức tạp hơn có lẽ không đáng để lưu không gian. –

+2

@FJ: 'z = {1: 'a', 2: 'b', 3: 'c'} [2 * ('a' trong x + y) + ('b' trong x + y)]' điều này thật thú vị ... – ChessMaster

+1

+1 để sáng tạo và có mã nhanh. –

15

Nếu ba mục trong câu hỏi là không "a", "b""c", nhưng thay vì 1, 23, bạn cũng có thể sử dụng một XOR nhị phân:

z = x^y 

Tổng quát hơn, nếu bạn muốn thiết lập z để còn lại một trong ba số a, bc cho hai số xy từ bộ này, bạn có thể sử dụng

z = x^y^a^b^c 

Tất nhiên bạn có thể precompute a^b^c nếu các số được cố định.

Cách tiếp cận này cũng có thể được sử dụng với các chữ cái gốc:

z = chr(ord(x)^ord(y)^96) 

Ví dụ:

>>> chr(ord("a")^ord("c")^96) 
'b' 

Đừng nghĩ rằng bất cứ ai đọc mã này ngay lập tức tìm ra ý nghĩa của nó :)

+0

+1 Giải pháp này có vẻ đẹp và thanh lịch; và nếu bạn làm cho nó có chức năng riêng của nó và cung cấp cho số ma thuật 96 một tên logic khá dễ theo dõi/duy trì ('xor_of_a_b_c = 96 # ord ('a')^ord ('b')^ord ('c') == 96'). Tuy nhiên, xét về tốc độ thô, tốc độ này chậm hơn khoảng 33% so với chuỗi dài của 'if/elif's; nhưng nhanh hơn 500% so với phương pháp 'set'. –

+0

@sven Cảm ơn bạn đã giới thiệu toán tử XOR cho tôi, Giải pháp của bạn sạch sẽ và thanh lịch, tôi nghĩ ví dụ này sẽ khiến nó dính vào não của tôi, Cảm ơn bạn lần nữa :) –

1

Sử dụng tính năng hiểu danh sách, giả sử như những người khác rằng một trong ba trường hợp trong mã của bạn nắm giữ:

l = ['a', 'b', 'c'] 
z = [n for n in l if n not in [x,y]].pop() 

Hoặc, như trong câu trả lời được chấp nhận, lợi dụng các tuple để giải nén nó,

z, = [n for n in l if n not in [x,y]] 
28

mã tuyệt vời của Sven đã làm chỉ là một chút quá nhiều công việc và chould đã sử dụng tuple giải nén thay vì pop (). Ngoài ra, nó có thể đã thêm một bảo vệ if x != y để kiểm tra xy là khác biệt.Dưới đây là những gì các câu trả lời được cải thiện trông giống như:

# create the set just once 
choices = {'a', 'b', 'c'} 

x = 'a' 
y = 'b' 

# the main code can be used in a loop 
if x != y: 
    z, = choices - {x, y} 

Sau đây là các timings so sánh với một bộ thời gian để hiển thị các hiệu suất tương đối:

import timeit, itertools 

setup_template = ''' 
x = %r 
y = %r 
choices = {'a', 'b', 'c'} 
''' 

new_version = ''' 
if x != y: 
    z, = choices - {x, y} 
''' 

original_version = ''' 
if x == 'a' and y == 'b' or x == 'b' and y == 'a': 
    z = 'c' 
elif x == 'b' and y == 'c' or x == 'c' and y == 'b': 
    z = 'a' 
elif x == 'a' and y == 'c' or x == 'c' and y == 'a': 
    z = 'b' 
''' 

for x, y in itertools.product('abc', repeat=2): 
    print '\nTesting with x=%r and y=%r' % (x, y) 
    setup = setup_template % (x, y) 
    for stmt, name in zip([original_version, new_version], ['if', 'set']): 
     print min(timeit.Timer(stmt, setup).repeat(7, 100000)), 
     print '\t%s_version' % name 

Dưới đây là kết quả của timings:

Testing with x='a' and y='a' 
0.0410830974579  original_version 
0.00535297393799 new_version 

Testing with x='a' and y='b' 
0.0112571716309  original_version 
0.0524711608887  new_version 

Testing with x='a' and y='c' 
0.0383319854736  original_version 
0.048309803009  new_version 

Testing with x='b' and y='a' 
0.0175108909607  original_version 
0.0508949756622  new_version 

Testing with x='b' and y='b' 
0.0386209487915  original_version 
0.00529098510742 new_version 

Testing with x='b' and y='c' 
0.0259420871735  original_version 
0.0472128391266  new_version 

Testing with x='c' and y='a' 
0.0423510074615  original_version 
0.0481910705566  new_version 

Testing with x='c' and y='b' 
0.0295209884644  original_version 
0.0478219985962  new_version 

Testing with x='c' and y='c' 
0.0383579730988  original_version 
0.00530385971069 new_version 

Những thời gian này cho thấy hiệu suất phiên bản gốc thay đổi một chút tùy thuộc vào các câu lệnh if được kích hoạt d bởi các giá trị đầu vào khác nhau.

+2

Bài kiểm tra của bạn có vẻ sai lệch. Cái gọi là "set_version" chỉ đôi khi nhanh hơn vì nó được bảo vệ bởi câu lệnh 'if' bổ sung. – ekhumoro

+2

@ekhumoro Đó là những gì các đặc điểm kỹ thuật vấn đề được gọi là: "kiểm tra nếu * x * và * y * là giá trị riêng biệt và nếu như vậy, đặt * z * cho nhân vật thứ ba". Cách nhanh nhất (và thẳng tiến nhất) để kiểm tra xem các giá trị có khác biệt hay không là '' x! = Y''. Chỉ khi chúng khác biệt chúng ta có sự khác biệt thiết lập để xác định ký tự thứ ba :-) –

+2

Điểm tôi đã làm là các phép thử của bạn không cho thấy 'set_version' thực hiện tốt hơn _bởi vì nó được dựa trên sets_; nó chỉ hoạt động tốt hơn vì câu lệnh 'if' bảo vệ. – ekhumoro

47

Phương pháp strip là một tùy chọn chạy một cách nhanh chóng đối với tôi: "Có"

z = 'abc'.strip(x+y) if x!=y else None 
+2

+1 Nó cũng rất minh bạch, và không giống như hầu hết các câu trả lời, nó đề cập đến x == y. –

+1

Ý tưởng hay, +1; mặc dù tôi thực sự nghĩ rằng '" a "', '" b "' và '" c "' trong bài gốc chỉ là phần giữ chỗ cho các giá trị thực. Giải pháp này không tổng quát với bất kỳ loại giá trị nào khác so với các chuỗi có độ dài 1. –

+0

@chepner thats creative! Cảm ơn bạn đã trả lời chepner. –

0

Xem nếu điều này làm việc

if a not in xy 
    z= 'a' 
if b not in xy 
    z='b' 
if c not in xy 
    z='c' 
+0

Không, không .. –

Các vấn đề liên quan