เหตุใด Quicksort จึงดีกว่าอัลกอริธึมการเรียงลำดับอื่น ๆ ในทางปฏิบัติ


31

นี่คือ repost ของคำถามเกี่ยวกับ cs.SEโดยJanoma หน่วยกิตและเครดิตทั้งหมดของเขาหรือของซีเอส

ในหลักสูตรอัลกอริธึมมาตรฐานเราถูกสอนว่าquicksortคือ O (n log n) โดยเฉลี่ยและ O (n²) ในกรณีที่แย่ที่สุด ในเวลาเดียวกันอัลกอริธึมการเรียงลำดับอื่น ๆ กำลังศึกษาซึ่งเป็น O (n log n) ในกรณีที่เลวร้ายที่สุด (เช่นการผสานและheapsort ) และแม้แต่เวลาเชิงเส้นในกรณีที่ดีที่สุด (เช่นbubbleort ) แต่มีความต้องการหน่วยความจำเพิ่มเติม

หลังจากมองผ่าน ๆ ในเวลาที่วิ่งเร็วขึ้นมันเป็นเรื่องธรรมดาที่จะบอกว่า quicksort ไม่ควรมีประสิทธิภาพเหมือนกับคนอื่น ๆ

นอกจากนี้ให้พิจารณาว่านักเรียนเรียนรู้ในหลักสูตรการเขียนโปรแกรมขั้นพื้นฐานที่การเรียกซ้ำโดยทั่วไปไม่ดีนักเพราะอาจใช้หน่วยความจำมากเกินไป ฯลฯ ดังนั้น (และแม้ว่านี่จะไม่ใช่ข้อโต้แย้งจริง ๆ ) สิ่งนี้ทำให้เกิดความคิดว่า ดีจริงๆเพราะเป็นอัลกอริทึมแบบเรียกซ้ำ

เหตุใด quicksort จึงมีประสิทธิภาพสูงกว่าอัลกอริทึมการเรียงลำดับอื่น ๆ ในทางปฏิบัติ มันเกี่ยวข้องกับโครงสร้างของข้อมูลจริงหรือไม่? มันเกี่ยวข้องกับการทำงานของหน่วยความจำในคอมพิวเตอร์หรือไม่? ฉันรู้ว่าความทรงจำบางอย่างนั้นเร็วกว่าวิธีอื่น ๆ แต่ฉันไม่รู้ว่านั่นเป็นเหตุผลที่แท้จริงสำหรับประสิทธิภาพการตอบโต้ที่ใช้งานง่ายนี้หรือไม่ (เมื่อเปรียบเทียบกับการประเมินเชิงทฤษฎี)


3
ชื่อเสียง Quicksort นับจากเวลาที่ไม่มีแคชอยู่
AProgrammer

9
"เหตุใด quicksort จึงมีประสิทธิภาพสูงกว่าอัลกอริทึมการเรียงลำดับอื่น ๆ ในทางปฏิบัติ" จริงเหรอ? แสดงให้เราเห็นถึงการนำไปปฏิบัติจริงที่คุณอ้างถึงด้วยคำชี้แจงนี้และชุมชนจะบอกคุณว่าเหตุใดการดำเนินการเฉพาะนั้นจึงมีลักษณะที่เป็นเช่นนั้น ทุกสิ่งทุกอย่างจะนำไปสู่การคาดเดาเกี่ยวกับโปรแกรมที่ไม่มีอยู่จริง
Doc Brown

1
@DocBrown: การใช้งาน Quicksort จำนวนมาก (หรือหลายรูปแบบ) ได้รับการคัดเลือกในห้องสมุดหลายแห่งเนื่องจากพวกเขาทำงานได้ดีที่สุด (ฉันหวังว่าจะเป็นเช่นนั้น) ดังนั้นจึงมีเพียงแค่อาจจะมีอะไรบางอย่างเกี่ยวกับขั้นตอนวิธีการที่ทำให้ Quicksort รวดเร็วเป็นอิสระจากการดำเนินงาน
Raphael

1
มีคนพูดแบบนี้เพื่อความสมบูรณ์ดังนั้นฉันจะ: Quicksort ไม่เสถียร (ปกติ) ด้วยเหตุผลนี้คุณอาจไม่ต้องการใช้งาน นอกจากนี้ด้วยเหตุผลนี้การเรียงลำดับเริ่มต้นของคุณอาจไม่ใช่ Quicksort แม้ว่าจะเป็นสิ่งที่คุณต้องการก็ตาม
RalphChapin

1
@ ราฟาเอล: บ่อยครั้งที่สิ่งที่เรียกว่าการจัดเรียงอย่างรวดเร็วจริง ๆ แล้วการเปลี่ยนแปลงบางอย่างเช่นการจัดเรียงภายใน (ใช้, afaik, ในห้องสมุดมาตรฐาน C ++) ไม่ใช่การจัดเรียงอย่างรวดเร็วบริสุทธิ์
Giorgio

คำตอบ:


21

ฉันจะไม่เห็นด้วยว่า quicksort นั้นดีกว่าอัลกอริทึมการเรียงลำดับอื่น ๆ ในทางปฏิบัติ

เพื่อจุดประสงค์ส่วนใหญ่Timsort - ไฮบริดระหว่างการเรียงลำดับการผสาน / การแทรกซึ่งใช้ประโยชน์จากข้อเท็จจริงที่ว่าข้อมูลที่คุณเรียงลำดับมักจะเริ่มเรียงเกือบหรือเรียงกลับกัน

quicksort ที่ง่ายที่สุด (ไม่มี pivot สุ่ม) ถือว่ากรณีที่เป็นไปได้นี้เป็น O (N ^ 2) (การลดลงถึง O (N lg N) ด้วยการหมุนแบบสุ่ม) ในขณะที่ TimSort สามารถจัดการเคสเหล่านี้ใน O (N)

อ้างอิงจากมาตรฐานเหล่านี้ใน C # เปรียบเทียบquicksort ในตัวกับ TimSort ทำให้ Timsort เร็วขึ้นอย่างมากในกรณีที่เรียงลำดับส่วนใหญ่และเร็วกว่าเล็กน้อยใน data data case และ TimSort ดีขึ้นหากฟังก์ชันการเปรียบเทียบช้าโดยเฉพาะ ฉันไม่ได้ทำซ้ำเกณฑ์มาตรฐานเหล่านี้และจะไม่แปลกใจถ้า quicksort เอาชนะ TimSort เล็กน้อยสำหรับการรวมกันของข้อมูลแบบสุ่มหรือหากมีบางสิ่งที่แปลกในการจัดเรียง builtin ของ C # (ขึ้นอยู่กับ quicksort) ที่ทำให้ช้าลง อย่างไรก็ตาม TimSort มีข้อได้เปรียบที่แตกต่างกันเมื่อข้อมูลอาจถูกจัดเรียงบางส่วนและมีค่าเท่ากับการเรียงลำดับอย่างรวดเร็วในแง่ของความเร็วเมื่อข้อมูลไม่ได้ถูกจัดเรียงบางส่วน

TimSort ยังมีโบนัสเพิ่มของการเรียงลำดับที่เสถียรไม่เหมือน quicksort ข้อเสียเพียงอย่างเดียวของ TimSort ที่ใช้หน่วยความจำ O (N) กับ O (lg N) ในการใช้งานปกติ (เร็ว)


18

การจัดเรียงแบบเร็วนั้นถือว่าเร็วกว่าเนื่องจากค่าสัมประสิทธิ์มีขนาดเล็กกว่าอัลกอริทึมที่รู้จักอื่น ๆ ไม่มีเหตุผลหรือข้อพิสูจน์สำหรับสิ่งนั้นเพียงแค่ไม่พบอัลกอริทึมที่มีค่าสัมประสิทธิ์น้อยลง ความจริงที่ว่าอัลกอริธึมอื่นมีเวลา O ( n log n ) แต่ในโลกแห่งความจริงสัมประสิทธิ์ก็มีความสำคัญเช่นกัน

โปรดทราบว่าสำหรับการเรียงลำดับการแทรกข้อมูลขนาดเล็ก (อันที่ถือว่าเป็น O ( n 2 )) นั้นเร็วกว่าเนื่องจากลักษณะของฟังก์ชันทางคณิตศาสตร์ ขึ้นอยู่กับค่าสัมประสิทธิ์เฉพาะที่แตกต่างกันไปในแต่ละเครื่อง (ในตอนท้ายแอสเซมบลีเท่านั้นกำลังทำงานจริง ๆ ) ดังนั้นบางครั้งไฮบริดของเรียงลำดับอย่างรวดเร็วและเรียงแทรกเป็นวิธีที่เร็วที่สุดในทางปฏิบัติที่ฉันคิด


7
+ ขวา ครูต้องตระหนักมากขึ้น (และฉันก็เป็นครู) เกี่ยวกับความจริงที่ว่าปัจจัยคงที่สามารถเปลี่ยนแปลงได้ตามลำดับความสำคัญ ดังนั้นทักษะในการปรับแต่งประสิทธิภาพจึงมีความสำคัญจริงๆโดยไม่คำนึงถึง big-O ปัญหาคือพวกเขายังคงสอนgprofเพียงเพราะพวกเขาต้องผ่านจุด bullet นั้นในหลักสูตรซึ่งเป็นแนวทางที่ผิด 180 องศา
Mike Dunlavey

2
“ ไม่มีเหตุผลหรือโปร [o] f สำหรับสิ่งนั้น”: แน่ใจว่ามี หากคุณขุดลึกลงไปคุณจะพบเหตุผล
Gilles 'หยุดความชั่วร้าย'

2
@B Seven: เพื่อให้ง่ายขึ้นมาก ... สำหรับอัลกอริทึมการเรียงลำดับ O (n log n) มีการวนซ้ำ (n log n) ของลูปการเรียงลำดับเพื่อเรียงรายการ n สัมประสิทธิ์คือระยะเวลาของแต่ละรอบของลูป เมื่อ n มีค่ามาก (อย่างน้อยหลายพันค่า) สัมประสิทธิ์ไม่สำคัญเท่ากับ O () แม้ว่าค่าสัมประสิทธิ์จะมาก แต่เมื่อ n มีขนาดเล็กสัมประสิทธิ์มีความสำคัญ - และอาจเป็นสิ่งที่สำคัญที่สุดหากคุณเรียงลำดับ 10 รายการเท่านั้น
Matt Gallagher

4
@ MikeDunlavey - ตัวอย่างที่ดีคือการสร้างปิรามิดคือ O (n) ในขณะที่การเรียงลำดับรูปภาพของคุณคือ O (n ln n) แต่เร็วกว่า!
Martin Beckett

2
มีการรับประกันอัลกอริทึม O (n log n) เช่น heapsort และการรวมดังนั้นในเงื่อนไขที่เลวร้ายที่สุดกรณีที่ไม่มีอาการ Quicksort ไม่เท่ากันเร็วที่สุด แต่ในโลกแห่งความเป็นจริงตัวแปร quicksort บางตัวทำได้ดีมาก อย่างไรก็ตามการพูดว่า "สัมประสิทธิ์น้อยลง" ก็เหมือนกับการพูดว่า "เร็วกว่าเพราะเร็วกว่า" ทำไมปัจจัยคงที่มีขนาดเล็กมาก เหตุผลสำคัญคือเนื่องจาก quicksort ดีมากในแง่ของพื้นที่ - ใช้ประโยชน์จากแคชได้เป็นอย่างดี mergesort มีท้องที่ดีเกินไป แต่ก็มากยากที่จะทำในสถานที่
Steve314

16

Quicksort ไม่ได้มีประสิทธิภาพสูงกว่าอัลกอริทึมการเรียงลำดับอื่น ๆ ทั้งหมด ตัวอย่างเช่นการเรียงลำดับฮีพจากล่างขึ้นบน ( Wegener 2002 ) มีประสิทธิภาพสูงกว่า quicksort สำหรับปริมาณข้อมูลที่สมเหตุสมผลและยังเป็นอัลกอริทึมแบบแทนที่ นอกจากนี้ยังง่ายต่อการใช้งาน (อย่างน้อยไม่ยากกว่าบางตัวแปรด่วนที่ปรับให้เหมาะสม)

มันไม่ได้เป็นที่รู้จักกันดีและคุณไม่พบมันในหนังสือเรียนหลายเล่มซึ่งอาจอธิบายได้ว่าทำไมมันจึงไม่ได้รับความนิยมอย่างรวดเร็ว


+1: ฉันได้ทำการทดสอบและการเรียงผสานจริง ๆ แล้วดีกว่าการเรียงอย่างรวดเร็วสำหรับอาร์เรย์ขนาดใหญ่ (> องค์ประกอบ 100,000) การเรียงลำดับฮีปนั้นแย่กว่าการรวมการผสานเล็กน้อย (แต่การเรียงแบบผสานต้องการหน่วยความจำเพิ่มเติม) ฉันคิดว่าสิ่งที่ผู้คนเรียกว่าการเรียงลำดับอย่างรวดเร็วมักจะเป็นรูปแบบที่เรียกว่าการเรียงลำดับล่วงหน้า: การจัดเรียงอย่างรวดเร็วซึ่งกลับไปที่การเรียงลำดับกองซ้อนเมื่อความลึกของการเรียกซ้ำเกินกว่าที่กำหนด
Giorgio

@Giorgio: quicksort สามารถแก้ไขได้ในบางวิธีเพื่อปรับปรุงดูตัวอย่างได้ที่นี่: algs4.cs.princeton.edu/23quicksortคุณลองปรับปรุงดูไหม?
Doc Brown

น่าสนใจคุณสามารถอ้างอิงถึงหนังสือ \ site เพื่ออ่านเพิ่มเติมเกี่ยวกับหนังสือเล่มนี้ได้หรือไม่? (ควรเป็นหนังสือ)
Ramzi Kahil

@ มาร์ติน: คุณหมายถึง heapsort จากล่างขึ้นบน? ฉันให้การอ้างอิงข้างต้น หากคุณต้องการแหล่งข้อมูลฟรีวิกิพีเดียภาษาเยอรมันมีบทความเกี่ยวกับเรื่องนี้ ( de.wikipedia.org/wiki/BottomUp-Heapsort ) แม้ว่าคุณจะไม่พูดภาษาเยอรมันฉันเดาว่าคุณยังสามารถอ่านตัวอย่างของ C99
Doc Brown

7

คุณไม่ควรมุ่งเน้นเฉพาะกรณีที่เลวร้ายที่สุดและซับซ้อนตามเวลาเท่านั้น มันเกี่ยวกับค่าเฉลี่ยมากกว่าที่เลวร้ายที่สุดและมันเกี่ยวกับเวลาและสถานที่

quicksort:

  • มีเวลาเฉลี่ยที่ซับซ้อนของΘ ( n log n );
  • สามารถนำไปใช้กับความซับซ้อนของพื้นที่ของΘ (log n );

นอกจากนี้ยังมีในบัญชีที่สัญกรณ์Oใหญ่ไม่ได้คำนึงถึงค่าคงที่ใด ๆ แต่ในทางปฏิบัติมันจะสร้างความแตกต่างถ้าอัลกอริทึมเร็วขึ้นสองเท่า Θ ( n log n ) หมายถึงขั้นตอนวิธีการที่รันในK  n  ล็อก ( n ) ที่Kเป็นค่าคงที่ quicksort เป็นขั้นตอนวิธีการเปรียบเทียบการจัดเรียงกับต่ำสุด K


1
@Gilles: มันมี K ต่ำเพราะมันเป็นอัลกอริธึมที่เรียบง่าย
vartec

5
WTF? มันไม่สมเหตุสมผลเลย ความเรียบง่ายของอัลกอริทึมไม่มีความสัมพันธ์กับความเร็วในการทำงาน การเรียงลำดับการเลือกนั้นง่ายกว่า quicksort ซึ่งไม่ได้ทำให้เร็วขึ้น
Gilles 'หยุดความชั่วร้าย' ใน

1
@Gilles: การเรียงลำดับการเลือกคือ O (n ^ 2) สำหรับกรณีใด ๆ (แย่ที่สุดค่าเฉลี่ยและดีที่สุด) ดังนั้นมันไม่สำคัญว่ามันจะง่ายแค่ไหน Quicksort คือ O (n log n) สำหรับกรณีทั่วไปและในบรรดา algos ทั้งหมดที่มี O (n log n) เฉลี่ยมันเป็นสิ่งที่ง่ายที่สุด
vartec

1
@Gilles: สิ่งอื่น ๆ ที่เท่าเทียมกันความเรียบง่ายช่วยให้มีประสิทธิภาพ สมมติว่าคุณกำลังเปรียบเทียบอัลกอริธึมสองตัวที่แต่ละตัวทำซ้ำ (K n log n) การวนซ้ำของลูปภายในที่เกี่ยวข้อง: อัลกอริทึมที่ต้องการทำสิ่งต่อลูปน้อยลงมีข้อได้เปรียบด้านประสิทธิภาพ
Comingstorm

1
@comingstorm: วลีเช่นที่ข้อความของคุณเป็นคำพูดที่ซ้ำซาก แต่ไม่เกี่ยวข้องกับ "ความเรียบง่าย" ยกตัวอย่างเช่นมีตัวแปรที่ซับซ้อนกว่าของ Quicksort (การแยกตัวพิมพ์เล็ก - ใหญ่) ซึ่งส่งผลให้รันไทม์เล็กลง (ทั้งในทางทฤษฎีและปฏิบัติ)
กราฟิลส์

5

Quicksort มักจะเป็นตัวเลือกที่ดีเนื่องจากมีความรวดเร็วและสมเหตุสมผลพอสมควรและง่ายต่อการใช้งาน

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


1

ประสิทธิภาพที่แท้จริงของอัลกอริทึมขึ้นอยู่กับแพลตฟอร์มรวมถึงภาษาคอมไพเลอร์ความสนใจของโปรแกรมเมอร์ในรายละเอียดการใช้งานความพยายามเพิ่มประสิทธิภาพเฉพาะและอื่น ๆ ดังนั้น "ข้อดีของปัจจัยคงที่อย่างรวดเร็ว" ของ quicksort นั้นไม่ได้กำหนดไว้อย่างดีนัก - มันเป็นการตัดสินใจเชิงอัตวิสัยตามเครื่องมือที่มีอยู่ในปัจจุบันและการประมาณคร่าวๆของ "ความพยายามในการใช้งานที่เท่าเทียมกัน" โดยใครก็ตาม .

ที่กล่าวว่าฉันเชื่อว่า quicksort ทำงานได้ดี (สำหรับการสุ่มอินพุต) เพราะมันง่ายและเนื่องจากโครงสร้างแบบเรียกซ้ำนั้นค่อนข้างเป็นมิตรกับแคช ในทางตรงกันข้ามเนื่องจากกรณีที่เลวร้ายที่สุดนั้นง่ายต่อการเรียกใช้การใช้งาน quicksort ใด ๆ จะต้องมีความซับซ้อนมากกว่าคำอธิบายตำราเรียนที่จะบ่งบอกถึง: ดังนั้นเวอร์ชันที่ปรับเปลี่ยนเช่น introsort

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


ฉันเดาว่าคนอื่น ๆ "คงที่น้อยลง" เกี่ยวข้องกับมันเป็นหนึ่งในการวิเคราะห์อย่างเป็นทางการที่อยู่ในจำนวนของการเปรียบเทียบหรือแลกเปลี่ยน สิ่งนี้ถูกนิยามไว้อย่างดี แต่ก็ไม่มีความชัดเจนว่าสิ่งนี้แปลเป็นรันไทม์อย่างไร เพื่อนร่วมงานของคุณทำการวิจัยเกี่ยวกับเรื่องนี้
Raphael

ความประทับใจของฉันคือว่ามันเกี่ยวกับการแสดงทั่วไป แต่ฉันก็ไม่เชื่อเช่นกัน แม้ว่าคุณจะถูกต้อง: หากการเปรียบเทียบของคุณมีราคาแพงเป็นพิเศษคุณสามารถค้นหาจำนวนการเปรียบเทียบที่คาดหวัง ...
Comingstorm

1
สำหรับเหตุผลที่คุณพูดคุยเกี่ยวกับประสิทธิภาพโดยรวม (เวลาที่เหมาะสม) ไม่ได้มีความหมายในกรณีทั่วไปเนื่องจากปัจจัยรายละเอียดมากเกินไปเหตุผลในการนับเฉพาะการดำเนินการเลือกไม่ได้ว่าพวกเขามีราคาแพง แต่พวกเขาเกิดขึ้น "บ่อยที่สุด "ในความหมายของ Landau-สัญกรณ์ (Big-Oh) ดังนั้นการนับสิ่งเหล่านี้จะทำให้คุณมีอาการเชิงเส้นคร่าวๆของคุณ ทันทีที่คุณพิจารณาค่าคงที่และ / หรือรันไทม์กลยุทธ์นี้น่าสนใจน้อยกว่ามาก
Raphael

การใช้งาน QuickSort ที่ดีจะรวบรวมเช่นว่าค่า pivot ของคุณยังคงอยู่ในการลงทะเบียน CPU ตราบใดที่จำเป็น นี่มักจะเพียงพอที่จะเอาชนะการเรียงลำดับที่เร็วกว่าในทางทฤษฎีเมื่อเทียบกับ Big-O คูณ
Dan Lyons

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