เหตุใดสูงสุดจึงช้ากว่าการเรียงลำดับ


92

ฉันพบว่าmaxช้ากว่าsortฟังก์ชันใน Python 2 และ 3

Python 2

$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 239 usec per loop
$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'max(a)'        
1000 loops, best of 3: 342 usec per loop

Python 3

$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 252 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a)'
1000 loops, best of 3: 371 usec per loop

ทำไมเป็น max ( O(n)) ช้ากว่าsortฟังก์ชั่น ( O(nlogn))?


3
คุณรันการวิเคราะห์ Python 2 ครั้งเดียวและรหัส Python 3 เหมือนกันทุกประการ
erip

9
a.sort()ทำงานในสถานที่ ลองsorted(a)
Andrea Corbellini

หากคุณแก้ไขแล้วโปรดโพสต์สิ่งที่คุณได้แก้ไขกลับมา
Pretzel

4
@Pretzel OP หมายถึงโพสต์ได้รับการแก้ไขไม่ใช่ว่าปัญหาได้รับการแก้ไขแล้ว
erip

2
@ WeizhongTu แต่sortเรียงแล้วaเรียงตลอด
njzk2

คำตอบ:


125

คุณต้องระมัดระวังอย่างมากเมื่อใช้timeitโมดูลใน Python

python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'

นี่คือรหัส initialisation aรันหนึ่งครั้งในการผลิตอาร์เรย์แบบสุ่ม จากนั้นโค้ดที่เหลือจะถูกรันหลาย ๆ ครั้ง ครั้งแรกที่จัดเรียงอาร์เรย์ แต่ทุกครั้งที่คุณเรียกใช้วิธีการจัดเรียงบนอาร์เรย์ที่เรียงลำดับแล้ว เฉพาะเวลาที่เร็วที่สุดเท่านั้นที่จะถูกส่งกลับดังนั้นคุณจึงกำหนดเวลาที่ Python ใช้เวลาในการเรียงลำดับอาร์เรย์แล้ว

ส่วนหนึ่งของอัลกอริทึมการจัดเรียงของ Python คือการตรวจจับเมื่ออาร์เรย์ถูกจัดเรียงบางส่วนหรือทั้งหมดแล้ว เมื่อจัดเรียงอย่างสมบูรณ์จะต้องสแกนหนึ่งครั้งผ่านอาร์เรย์เพื่อตรวจจับสิ่งนี้จากนั้นจึงหยุด

หากคุณลอง:

python -m timeit -s 'import random;a=range(100000);random.shuffle(a)' 'sorted(a)[-1]'

จากนั้นการเรียงลำดับจะเกิดขึ้นในทุกๆวงเวลาและคุณจะเห็นได้ว่าเวลาในการเรียงลำดับอาร์เรย์นั้นนานกว่าการหาค่าสูงสุด

แก้ไข: คำตอบของ @ skyking อธิบายส่วนที่ฉันทิ้งไว้โดยไม่ได้อธิบาย: a.sort()รู้ว่ามันทำงานในรายการเพื่อให้สามารถเข้าถึงองค์ประกอบได้โดยตรง max(a)ใช้งานได้กับการทำซ้ำตามอำเภอใจดังนั้นจึงต้องใช้การทำซ้ำทั่วไป


10
จับดี. ฉันไม่เคยตระหนักว่าสถานะของล่ามจะยังคงอยู่ตลอดการทำงานของโค้ด ตอนนี้ฉันสงสัยว่าที่ผ่านมาฉันสร้างเกณฑ์มาตรฐานผิดพลาดไปกี่ตัว : -}
Frerich Raabe

1
สิ่งนั้นชัดเจนสำหรับฉัน แต่สังเกตว่าแม้ว่าคุณจะเรียงอาร์เรย์ที่เรียงไว้แล้วคุณต้องตรวจสอบองค์ประกอบทั้งหมด ซึ่งก็เหมือนกับการได้รับสูงสุด .... สำหรับฉันนี่ดูเหมือนคำตอบเพียงครึ่งเดียว
Karoly Horvath

2
@KarolyHorvath คุณถูกต้อง ฉันคิดว่า @skyking มีคำตอบอีกครึ่งหนึ่ง: a.sort()รู้ว่ามันทำงานในรายการเพื่อให้สามารถเข้าถึงองค์ประกอบได้โดยตรง max(a)ทำงานตามลำดับโดยพลการเพื่อไม่ให้ใช้การทำซ้ำทั่วไป
Duncan

1
@KarolyHorvath บางทีการทำนายสาขาสามารถอธิบายได้ว่าทำไมการเรียงลำดับอาร์เรย์แบบเรียงลำดับซ้ำ ๆ จึงเร็วกว่า: stackoverflow.com/a/11227902/4600
marcospereira

1
@JuniorCompressor listsort.txtอธิบายว่า "มีประสิทธิภาพเหนือธรรมชาติในอาร์เรย์ที่สั่งซื้อบางส่วนหลายชนิด (จำเป็นต้องมีการเปรียบเทียบ lg (N!) น้อยกว่าและมีน้อยเท่ากับ N-1)" จากนั้นอธิบายการเพิ่มประสิทธิภาพของเลือดทุกชนิด ฉันคิดว่ามันสามารถสร้างสมมติฐานมากมายที่maxทำไม่ได้นั่นคือการเรียงลำดับจะไม่เร็วขึ้นอย่างไม่มีอาการ
Frerich Raabe

86

ปิดแรกทราบว่าmax()ใช้โปรโตคอลการทำซ้ำโดยในขณะที่รหัสเฉพาะกิจการใช้งานlist.sort() เห็นได้ชัดว่าการใช้ตัววนซ้ำเป็นค่าใช้จ่ายที่สำคัญนั่นคือเหตุผลที่คุณสังเกตเห็นความแตกต่างในการกำหนดเวลา

อย่างไรก็ตามนอกเหนือจากนั้นการทดสอบของคุณยังไม่ยุติธรรม คุณกำลังทำงานa.sort()ในรายการเดียวกันมากกว่าหนึ่งครั้ง อัลกอริทึมที่ใช้โดยงูใหญ่ได้รับการออกแบบมาโดยเฉพาะเพื่อจะได้อย่างรวดเร็วสำหรับอยู่แล้ว (บางส่วน) เรียงข้อมูล การทดสอบของคุณบอกว่าอัลกอริทึมทำงานได้ดี

นี่คือการทดสอบที่ยุติธรรม:

$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a[:])'
1000 loops, best of 3: 227 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a[:].sort()'
100 loops, best of 3: 2.28 msec per loop

ที่นี่ฉันกำลังสร้างสำเนาของรายการทุกครั้ง อย่างที่คุณเห็นลำดับความสำคัญของผลลัพธ์แตกต่างกัน: ไมโครเทียบกับมิลลิวินาทีอย่างที่เราคาดหวัง

และจำไว้ว่า: big-Oh ระบุขอบเขตบน! ขอบเขตล่างสำหรับอัลกอริทึมการเรียงลำดับของ Python คือΩ ( n ) เป็น O ( n log n ) ไม่ได้หมายความว่าทุกคนโดยอัตโนมัติวิ่งยิงสัดส่วนเวลาที่จะnบันทึกn ไม่ได้หมายความว่าจะต้องช้ากว่าอัลกอริทึมO ( n ) ด้วยซ้ำ แต่นั่นเป็นอีกเรื่องหนึ่ง สิ่งสำคัญที่ต้องเข้าใจก็คือในบางกรณีอัลกอริทึมO ( n log n ) อาจทำงานในเวลา O ( n ) หรือน้อยกว่า


31

อาจเป็นเพราะl.sortเป็นสมาชิกของlistwhile maxเป็นฟังก์ชันทั่วไป ซึ่งหมายความว่าl.sortสามารถอาศัยการแสดงภายในของlistwhile maxจะต้องผ่านโปรโตคอลตัววนซ้ำทั่วไป

นี้จะทำให้แต่ละองค์ประกอบที่ดึงข้อมูลสำหรับl.sortจะเร็วกว่าแต่ละองค์ประกอบเรียกว่าmaxไม่

ฉันคิดว่าถ้าคุณใช้แทนคุณจะได้รับผลที่ช้ากว่าsorted(a)max(a)


5
สมมติฐานนั้นเป็นเพียงช่วงเวลาเดียวที่จะกลายเป็นรูปธรรมมากขึ้น ไม่ได้ตั้งคำถามกับความรู้ของคุณเพียงแต่ว่าการเพิ่มเติมดังกล่าวเป็นเรื่องเล็กน้อยสำหรับการสาธิตของผู้ที่ไม่รู้
Reti43

คุณถูกต้องที่จะช้ากว่าsorted(a) max(a)ไม่น่าแปลกใจที่ความเร็วเท่าa.sort()กัน แต่การคาดเดาของคุณเกี่ยวกับสาเหตุที่ไม่เป็นเช่นนั้นเป็นเพราะ OP ทำผิดพลาดในการทดสอบตามที่ระบุไว้ในคำตอบที่ยอมรับ
martineau

ประเด็นคือมีความเป็นไปได้ที่โปรโตคอลวนซ้ำทั่วไปมีค่าใช้จ่ายเพียงพอที่จะหักล้างlog(n)ปัจจัยในความซับซ้อนได้ นั่นคือO(n)อัลกอริทึมเท่านั้นที่รับประกันได้ว่าจะเร็วกว่าO(nlogn)อัลกอริทึมที่มีขนาดใหญ่เพียงพอn(ตัวอย่างเช่นเนื่องจากเวลาในการดำเนินการแต่ละอย่างอาจแตกต่างกันระหว่างอัลกอริทึม - nlognขั้นตอนที่รวดเร็วอาจเร็วกว่าnขั้นตอนที่ช้า) ตรงที่ไม่ได้พิจารณาจุดคุ้มทุนในกรณีนี้ (แต่ควรทราบว่าlog nปัจจัยนั้นไม่ใช่ปัจจัยที่ใหญ่มากสำหรับ smallish n)
skyking
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.