มีวิธีแสดงที่หรูหรากว่านี้ ((x == a และ y == b) หรือ (x == b และ y == a) หรือไม่


108

ฉันพยายามประเมิน((x == a and y == b) or (x == b and y == a))ด้วย Python แต่ดูเหมือนว่าจะค่อนข้างละเอียด มีวิธีที่สง่างามกว่านี้ไหม?


8
ขึ้นอยู่กับประเภทของวัตถุx,y, a,b: พวกเขา ints / ลอย / สตริงวัตถุโดยพลการหรืออะไร หากเป็นประเภท builtin และเป็นไปได้ที่จะเก็บทั้งสองx,yและa,bเรียงลำดับแล้วคุณสามารถหลีกเลี่ยงสาขาที่สอง โปรดทราบว่าการสร้างชุดจะทำให้องค์ประกอบทั้งสี่นั้นx,y, a,bถูกแฮชซึ่งอาจหรือไม่เล็กน้อยหรือมีความหมายด้านประสิทธิภาพขึ้นอยู่กับประเภทของวัตถุ
smci

18
จำคนที่คุณทำงานด้วยและโครงการที่คุณกำลังพัฒนา ฉันใช้ Python เล็กน้อยที่นี่และตรงนั้น หากมีคนเขียนคำตอบอย่างใดอย่างหนึ่งที่นี่ฉันจะต้อง google สิ่งที่เกิดขึ้น เงื่อนไขของคุณสามารถอ่านได้ค่อนข้างมากไม่ว่าจะเกิดอะไรขึ้น
Areks

3
ฉันจะไม่รำคาญกับการเปลี่ยนใด ๆ มันสั้นกว่า แต่ไม่ชัดเจน (IMHO) และฉันคิดว่าทุกอย่างจะช้าลง
Barmar

22
นี่เป็นปัญหา XYAB แบบคลาสสิก
Tashus

5
และโดยทั่วไปหากคุณกำลังจัดการกับคอลเลกชันของวัตถุตามลำดับให้ใช้ชุด / dicts / ฯลฯ นี่เป็นปัญหา XY จริงๆเราต้องดู codebase ที่ใหญ่กว่านี้ ฉันเห็นด้วยที่((x == a and y == b) or (x == b and y == a))อาจดูเหมือน yukky แต่ 1) เจตนาของมันชัดเจนและเข้าใจได้ง่ายสำหรับโปรแกรมเมอร์ที่ไม่ใช่ Python ไม่ใช่ cryptic 2) interpreters / compilers จะจัดการได้ดี ทางเลือก ดังนั้น 'สง่างามยิ่งขึ้น' ก็อาจมีข้อเสียร้ายแรงเช่นกัน
smci

คำตอบ:


151

หากองค์ประกอบนั้น hashable คุณสามารถใช้ชุด:

{a, b} == {y, x}

18
@ Graham ไม่มันไม่ได้เพราะมีสองรายการในชุดมือขวา ถ้าทั้งคู่เป็น a, ไม่มี b และถ้าทั้งคู่เป็น b, ไม่มี a และในกรณีใดกรณีหนึ่งชุดจะไม่เท่ากัน
ฮอบส์

12
ในทางกลับกันถ้าเรามีองค์ประกอบสามด้านในแต่ละด้านและเราจำเป็นต้องทดสอบว่าพวกเขาสามารถจับคู่กับหนึ่งต่อหนึ่งแล้วชุดจะไม่ทำงาน {1, 1, 2} == {1, 2, 2}. ณ จุดที่คุณต้องการหรือsorted Counter
user2357112 รองรับ Monica

11
ฉันพบว่ามันยากที่จะอ่าน (อย่าอ่าน "{}" เป็น "()") เพียงพอที่จะแสดงความคิดเห็น - แล้วจุดประสงค์จะหายไป
Édouard

9
ฉันรู้ว่ามันเป็นงูหลาม แต่การสร้างสองชุดใหม่เพื่อเปรียบเทียบค่าดูเหมือนว่าเกินความจริงสำหรับฉัน ... เพียงแค่กำหนดฟังก์ชั่นที่ทำมันและโทรเมื่อจำเป็น
marxin

5
@marxin ฟังก์ชั่นจะยิ่งใหญ่กว่าใหญ่กว่า 2 ชุดโครงสร้างธรรมดา และอ่านน้อยลงด้วย 4 อาร์กิวเมนต์
Gloweye

60

ฉันคิดว่าสิ่งที่ดีที่สุดที่คุณจะทำได้คือบรรจุพวกมันไว้ในสิ่งอันดับ:

if (a, b) == (x, y) or (a, b) == (y, x)

หรืออาจห่อในการค้นหาชุด

if (a, b) in {(x, y), (y, x)}

เนื่องจากมีการพูดถึงความคิดเห็นสองสามครั้งฉันจึงกำหนดเวลาไว้และ tuples และชุดปรากฏขึ้นเพื่อทำงานเหมือนกันที่นี่เมื่อการค้นหาล้มเหลว:

from timeit import timeit

x = 1
y = 2
a = 3
b = 4

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
32.8357742

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
31.6169182

แม้ว่า tuples จะเร็วขึ้นจริงเมื่อค้นหาสำเร็จ:

x = 1
y = 2
a = 1
b = 2

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
35.6219458

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
27.753138700000008

ฉันเลือกที่จะใช้ชุดเพราะฉันทำการค้นหาความเป็นสมาชิกและแนวความคิดชุดที่เหมาะสำหรับกรณีการใช้งานที่ดีกว่า tuple หากคุณวัดความแตกต่างอย่างมีนัยสำคัญระหว่างโครงสร้างทั้งสองในกรณีการใช้งานเฉพาะให้ไปกับโครงสร้างที่เร็วกว่า ฉันไม่คิดว่าประสิทธิภาพเป็นปัจจัยที่นี่


12
วิธี tuple ดูสะอาดมาก ฉันกังวลเกี่ยวกับประสิทธิภาพของการใช้ชุด คุณสามารถทำได้if (a, b) in ((x, y), (y, x))ไหม
Brilliand

18
@Brilliand หากคุณกังวลเรื่องผลกระทบต่อประสิทธิภาพ Python ไม่ใช่ภาษาที่เหมาะกับคุณ :-D
Sneftel

ฉันชอบวิธีแรกมากการเปรียบเทียบทูเปิลสองครั้ง ง่ายต่อการแยกวิเคราะห์ (หนึ่งประโยคในแต่ละครั้ง) และแต่ละประโยคง่ายมาก และที่สำคัญคือควรมีประสิทธิภาพ
Matthieu M.

มีเหตุผลใดที่จะชอบsetทางออกในคำตอบของ tuple solution จาก @Brilliand?
user1717828

ชุดต้องการวัตถุที่ hashable ดังนั้น tuple จะเป็นวิธีแก้ปัญหาทั่วไปที่มีการพึ่งพาน้อยลง ประสิทธิภาพในขณะที่อาจไม่สำคัญโดยทั่วไปอาจมีลักษณะแตกต่างกันมากเมื่อจัดการกับวัตถุขนาดใหญ่ที่มีการตรวจสอบการแปลงแป้นพิมพ์และการตรวจสอบความเท่าเทียมกัน
Dukeling

31

สิ่งอันดับทำให้อ่านได้ง่ายขึ้นเล็กน้อย:

(x, y) == (a, b) or (x, y) == (b, a)

สิ่งนี้ให้เบาะแส: เรากำลังตรวจสอบว่าลำดับx, yมีค่าเท่ากับลำดับa, bแต่ไม่สนใจการสั่งซื้อหรือไม่ นั่นเป็นเพียงการตั้งค่าความเท่าเทียมกัน!

{x, y} == {a, b}

,สร้าง tuple ไม่ใช่รายการ เพื่อให้(x, y)และ(a, b)เป็นอันดับเช่นเดียวกับและx, y a, b
kissgyorgy

ฉันหมายถึง "รายการ" ในแง่ของ "ลำดับขององค์ประกอบที่สั่ง" ไม่ใช่ในความหมายของlistประเภทPython แก้ไขแล้วเพราะนี่ทำให้สับสน
โทมัส

27

หากรายการไม่แฮช แต่ให้การสนับสนุนการเปรียบเทียบการสั่งซื้อคุณสามารถลอง:

sorted((x, y)) == sorted((a, b))

เนื่องจากมันใช้ได้กับไอเท็มที่แฮชได้เช่นกัน (ใช่มั้ย) มันเป็นคำตอบระดับโลก
Carl Witthoft

5
@CarlWitthoft: ไม่มีประเภทที่ hashable แต่ไม่สามารถเรียงลำดับได้: complexตัวอย่างเช่น
dan04

26

วิธีที่หรูหราที่สุดในความคิดของฉันคือ

(x, y) in ((a, b), (b, a))

นี่เป็นวิธีที่ดีกว่าการใช้เซตเช่น{a, b} == {y, x}ตามที่ระบุในคำตอบอื่น ๆ เพราะเราไม่จำเป็นต้องคิดว่าตัวแปรนั้นมีแฮช


สิ่งนี้แตกต่างจากคำตอบก่อนหน้านี้อย่างไร
scohe001

5
@ scohe001 มันใช้ tuple ที่คำตอบก่อนหน้านี้ใช้ชุด คำตอบก่อนหน้านั้นพิจารณาคำตอบนี้ แต่ปฏิเสธที่จะแสดงว่าเป็นคำแนะนำ
Brilliand

25

(x+y)==(a+b) and (x*y)==(a*b)ถ้าสิ่งเหล่านี้เป็นตัวเลขที่คุณสามารถใช้

min(x,y)==min(a,b) and max(x,y)==max(a,b)ถ้าสิ่งเหล่านี้เป็นรายการที่เทียบเคียงคุณสามารถใช้

แต่((x == a and y == b) or (x == b and y == a))ชัดเจนปลอดภัยและทั่วๆไป


2
ฮ่า ๆ ๆ ชื่อพหุนาม ftw!
Carsten S

2
ฉันคิดว่าสิ่งนี้สร้างความเสี่ยงของข้อผิดพลาดที่ล้น
Razvan Socol

3
นี่คือคำตอบที่ถูกต้องโดยเฉพาะอย่างยิ่งประโยคสุดท้าย ทำให้มันง่ายนี่ไม่ควรจะต้องใช้มันซ้ำหลายตัวหรืออะไรทำนองนั้น สำหรับผู้ที่ต้องการใช้ชุดลองดูที่การใช้งานชุดวัตถุแล้วจินตนาการว่าพยายามเรียกใช้ชุดนี้ในวงที่แคบ ...
Z4-tier

3
@RazvanSocol OP ไม่ได้บอกประเภทของข้อมูลและคำตอบนี้มีคุณสมบัติตรงตามประเภทของโซลูชั่น
ชั้น Z4

21

เป็นการวางนัยให้กับตัวแปรมากกว่าสองตัวที่เราสามารถitertools.permutationsใช้ได้ นั่นคือแทน

(x == a and y == b and z == c) or (x == a and y == c and z == b) or ...

เราสามารถเขียน

(x, y, z) in itertools.permutations([a, b, c])

และแน่นอนว่ารุ่นตัวแปรสองตัว:

(x, y) in itertools.permutations([a, b])

5
คำตอบที่ดี มันคุ้มค่าที่จะชี้ให้เห็นที่นี่ (สำหรับผู้ที่ไม่ได้ทำอะไรกับเครื่องกำเนิดไฟฟ้ามาก่อน) ว่านี่เป็นหน่วยความจำที่มีประสิทธิภาพมากเนื่องจากมีการสร้างการเปลี่ยนแปลงครั้งเดียวครั้งเดียวและการตรวจสอบ "ใน" จะหยุดและกลับ True ทันทีหลังจาก พบการแข่งขัน
robertlayton

2
มันก็คุ้มค่าที่ชี้ให้เห็นว่าความซับซ้อนของวิธีนี้คือO(N*N!); สำหรับตัวแปร 11 ตัวอาจใช้เวลานานกว่าหนึ่งวินาทีในการเสร็จสิ้น (ฉันโพสต์วิธีที่เร็วกว่า แต่ก็ยังใช้O(N^2)งานได้และเริ่มใช้เวลาที่สองในตัวแปร 10k ดังนั้นจึงดูเหมือนว่าจะทำได้อย่างรวดเร็วหรือโดยทั่วไป (wrt. hashability / orderability) แต่ไม่ใช่ทั้งสอง: P)
Aleksi Torhamo

15

คุณสามารถใช้สิ่งอันดับเพื่อแสดงข้อมูลของคุณจากนั้นตรวจสอบการรวมชุดเช่น:

def test_fun(x, y):
    test_set = {(a, b), (b, a)}

    return (x, y) in test_set

3
เขียนเป็นแบบเขียนเดี่ยวและมีรายการ (สำหรับรายการที่ไม่สามารถแฮชได้) ฉันจะถือว่านี่เป็นคำตอบที่ดีที่สุด (แม้ว่าฉันจะเป็นมือใหม่ Python ที่สมบูรณ์)
Édouard

1
@ Édouardตอนนี้มีคำตอบอีกข้อหนึ่งที่สำคัญ (แค่ใช้ tuple แทนที่จะเป็นรายการซึ่งมีประสิทธิภาพมากกว่าอยู่แล้ว)
Brilliand

10

คุณมีวิธีแก้ไขปัญหาที่อ่านได้มากที่สุดแล้วแล้ว มีวิธีอื่นในการแสดงสิ่งนี้อาจมีตัวอักษรน้อยลง แต่มีน้อยกว่าในการอ่าน

ทั้งนี้ขึ้นอยู่กับสิ่งที่มีค่าจริงแทนทางออกที่ดีที่สุดของคุณคือการตัดการตรวจสอบในการทำงานที่มีชื่อที่พูด หรือนอกจากนี้คุณสามารถจำลองวัตถุ x, y และ a, b ในวัตถุชั้นสูงโดยเฉพาะที่คุณสามารถเปรียบเทียบกับตรรกะของการเปรียบเทียบในวิธีการตรวจสอบความเท่าเทียมกันของคลาสหรือฟังก์ชันที่กำหนดเองโดยเฉพาะ


3

ดูเหมือนว่า OP จะเกี่ยวข้องกับกรณีของตัวแปรสองตัวเท่านั้น แต่เนื่องจาก StackOverflow สำหรับผู้ที่ค้นหาคำถามเดียวกันในภายหลังด้วยฉันจะพยายามจัดการกรณีทั่วไปที่นี่ในรายละเอียด หนึ่งคำตอบก่อนหน้านี้มีคำตอบทั่วไปที่ใช้อยู่itertools.permutations()แต่วิธีการนั้นนำไปสู่การO(N*N!)เปรียบเทียบเนื่องจากมีN!วิธีเรียงสับเปลี่ยนNแต่ละรายการ (นี่คือแรงจูงใจหลักสำหรับคำตอบนี้)

ก่อนอื่นเรามาสรุปว่าวิธีการบางอย่างในคำตอบก่อนหน้านี้นำไปใช้กับกรณีทั่วไปได้อย่างไรเป็นแรงจูงใจสำหรับวิธีที่นำเสนอที่นี่ ฉันจะใช้Aเพื่ออ้างถึง(x, y)และBอ้างถึง(a, b)ซึ่งอาจเป็นสิ่งอันดับความยาวตามอำเภอใจ (แต่เท่ากัน)

set(A) == set(B)รวดเร็ว แต่ใช้ได้เฉพาะในกรณีที่ค่าแฮชและคุณสามารถรับประกันได้ว่าหนึ่งใน tuples ไม่มีค่าที่ซ้ำกัน (เช่น.{1, 1, 2} == {1, 2, 2}ระบุโดย @ user2357112 ภายใต้คำตอบของ @Daniel Mesejo)

วิธีก่อนหน้านี้สามารถขยายเพื่อทำงานกับค่าที่ซ้ำกันโดยใช้พจนานุกรมที่มีการนับแทนชุด: (ซึ่งยังมีข้อ จำกัด ว่าค่าทั้งหมดจะต้อง hashable ดังนั้นเช่นค่าที่ไม่แน่นอนเช่นlistจะไม่ทำงาน)

def counts(items):
    d = {}
    for item in items:
        d[item] = d.get(item, 0) + 1
    return d

counts(A) == counts(B)

sorted(A) == sorted(B)ไม่ต้องใช้ค่าแฮช แต่จะช้ากว่าเล็กน้อยและต้องการค่าที่สามารถสั่งซื้อได้แทน (เช่นเช่นcomplexจะไม่ทำงาน)

A in itertools.permutations(B)ไม่ต้องการค่าแฮชหรือค่าสั่งซื้อ แต่อย่างที่กล่าวไปแล้วว่ามันมีO(N*N!)ความซับซ้อนดังนั้นแม้จะมีเพียง 11 รายการก็สามารถใช้เวลาหนึ่งวินาทีในการเสร็จสิ้น

ดังนั้นมีวิธีที่จะเป็นทั่วไป แต่เร็วขึ้นมาก? ทำไมใช่โดยการ "ตรวจสอบด้วยตนเอง" ว่ามีจำนวนเท่ากันในแต่ละรายการ: (ความซับซ้อนของอันนี้คือO(N^2)ดังนั้นสิ่งนี้จึงไม่ดีสำหรับอินพุตขนาดใหญ่เช่นกันบนเครื่องของฉันรายการ 10k อาจใช้เวลาไม่เกินวินาที - อินพุตที่เล็กลงเช่น 10 รายการนี่เร็วพอ ๆ กับรายการอื่น ๆ )

def unordered_eq(A, B):
    for a in A:
        if A.count(a) != B.count(a):
            return False
    return True

เพื่อให้ได้ประสิทธิภาพที่ดีที่สุดเราอาจต้องการลองdictใช้เมธอด -based ก่อนถอยกลับไปที่sortedเมธอด -based หากล้มเหลวเนื่องจากค่า unhashable และในที่สุดก็กลับไปcountใช้เมธอด -based หากล้มเหลวเนื่องจากค่าที่ไม่สามารถจัดลำดับได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.