เหตุใด Collections.sort จึงใช้การจัดเรียงแบบผสานแทนที่จะเป็น Quicksort


101

เราทราบดีว่าการจัดเรียงแบบรวดเร็วเป็นอัลกอริทึมการเรียงลำดับที่เร็วที่สุด

JDK6 collections.sortใช้อัลกอริธึมการเรียงลำดับผสานแทนการเรียงลำดับด่วน แต่ Arrays.sort ใช้อัลกอริทึมการจัดเรียงอย่างรวดเร็ว

อะไรคือเหตุผลที่ Collections.sort ใช้การเรียงลำดับผสานแทนการเรียงลำดับด่วน


3
หากคุณไม่สามารถให้ผู้เขียน JDK ตอบได้ทั้งหมดที่คุณจะได้รับก็คือการคาดเดา ไม่ใช่คำถามที่แท้จริง
Marquis of Lorne

4
@EJP จุดดี แต่แน่นอน "ไม่สร้างสรรค์" คือเหตุผลในการปิดที่ถูกต้อง เป็นที่ชัดเจนสำหรับฉันว่าคำถามคืออะไร
Duncan Jones

2
เพราะพวก Java ตัดสินใจทำแบบนี้ ถามพวกเขา. คุณไม่สามารถรับคำตอบที่ถูกต้องได้ที่นี่ฉันคิดว่า และการเรียงลำดับอย่างรวดเร็วไม่ใช่สิ่งที่ดีที่สุด เหมาะสำหรับการใช้งานทั่วไปเท่านั้น
Adam Arold

4
หนึ่งเดา: Quicksort ไม่เสถียร Mergesort คือ สำหรับแบบดั้งเดิมการจัดเรียงแบบเสถียร / ไม่คงที่ไม่เกี่ยวข้องสำหรับอ็อบเจ็กต์ที่อาจเป็นได้ (หรืออย่างน้อยคุณอาจได้รับข้อบกพร่องที่ยื่นต่อการจัดเรียงที่ไม่เสถียร)
parsifal

2
@EJP ไม่มีอะไรหยุดความตั้งใจของผู้เขียน JDK จากการเผยแพร่สู่สาธารณะ เมื่อเผยแพร่สู่สาธารณะแล้วเราไม่จำเป็นต้องให้ผู้เขียนตอบเอง ในความเป็นจริงเป็นไปได้ที่จะได้รับคำตอบที่เกินคาดเดาแม้จะไม่มีผู้เขียน JDK ตอบก็ตาม
Pacerier

คำตอบ:


188

มีแนวโน้มสูงจาก Josh Bloch § :

ฉันเขียนวิธีการเหล่านี้ดังนั้นฉันคิดว่าฉันมีคุณสมบัติที่จะตอบ เป็นความจริงที่ว่าไม่มีอัลกอริทึมการเรียงลำดับที่ดีที่สุดเพียงวิธีเดียว QuickSort มีข้อบกพร่องที่สำคัญสองประการเมื่อเปรียบเทียบกับการผสาน:

  1. มันไม่คงที่ (ตามที่ parsifal ระบุไว้)

  2. ไม่รับประกันประสิทธิภาพ n log n; สามารถลดประสิทธิภาพเป็นกำลังสองของปัจจัยทางพยาธิวิทยา

ความเสถียรไม่ใช่ปัญหาสำหรับประเภทดั้งเดิมเนื่องจากไม่มีแนวคิดเรื่องอัตลักษณ์ที่แตกต่างจากความเท่าเทียมกัน (ค่า) และความเป็นไปได้ของพฤติกรรมกำลังสองถือว่าไม่เป็นปัญหาในทางปฏิบัติสำหรับการใช้งานของ Bentely และ McIlroy (หรือต่อมาสำหรับDual Pivot Quicksort ) ซึ่งเป็นสาเหตุที่ตัวแปร QuickSort เหล่านี้ใช้สำหรับประเภทดั้งเดิม

ความเสถียรเป็นเรื่องใหญ่เมื่อจัดเรียงวัตถุโดยพลการ ตัวอย่างเช่นสมมติว่าคุณมีวัตถุที่เป็นตัวแทนของข้อความอีเมลและคุณเรียงลำดับตามวันที่ก่อนแล้วตามผู้ส่ง คุณคาดว่าจะจัดเรียงตามวันที่ภายในผู้ส่งแต่ละราย แต่จะเป็นจริงก็ต่อเมื่อการจัดเรียงมีเสถียรภาพ นั่นเป็นเหตุผลที่เราเลือกที่จะจัดเรียงแบบคงที่ (Merge Sort) เพื่อจัดเรียงการอ้างอิงวัตถุ (ในทางเทคนิคการเรียงลำดับที่มีเสถียรภาพตามลำดับหลายรายการส่งผลให้มีการเรียงลำดับคำศัพท์บนคีย์ในลำดับย้อนกลับของประเภท: การเรียงลำดับสุดท้ายจะกำหนดคีย์ย่อยที่สำคัญที่สุด)

เป็นข้อดีที่ Merge Sort รับประกันประสิทธิภาพ n log n (เวลา) ไม่ว่าจะป้อนข้อมูลใดก็ตาม แน่นอนว่ามีข้อเสียอยู่: การเรียงลำดับอย่างรวดเร็วคือการจัดเรียงแบบ "ในสถานที่": มันต้องการเฉพาะบันทึก n พื้นที่ภายนอก (เพื่อรักษา call stack) ในทางกลับกันการผสานการจัดเรียงต้องใช้พื้นที่ภายนอก O (n) ตัวแปร TimSort (แนะนำใน Java SE 6) ต้องการพื้นที่น้อยลงอย่างมาก (O (k)) หากอาร์เรย์อินพุตเกือบจะเรียงลำดับ

นอกจากนี้สิ่งต่อไปนี้เกี่ยวข้อง:

อัลกอริทึมที่ใช้โดย java.util.Arrays.sort และ (ทางอ้อม) โดย java.util.Collections.sort เพื่อเรียงลำดับการอ้างอิงอ็อบเจ็กต์คือ "modified mergesort (ซึ่งการผสานจะถูกละไว้หากองค์ประกอบสูงสุดในรายการย่อยต่ำมีค่าน้อยกว่า องค์ประกอบที่ต่ำที่สุดในรายการย่อยระดับสูง) " เป็นการเรียงลำดับที่เสถียรอย่างรวดเร็วพอสมควรซึ่งรับประกันประสิทธิภาพ O (n log n) และต้องการพื้นที่พิเศษ O (n) ในสมัยนั้น (เขียนในปี 1997 โดย Joshua Bloch) มันเป็นทางเลือกที่ดี แต่วันนี้เราทำได้ดีกว่ามาก

ตั้งแต่ปี 2546 การจัดเรียงรายการของ Python ได้ใช้อัลกอริทึมที่เรียกว่า timsort (หลังจาก Tim Peters ผู้เขียน) เป็นการผสานที่เสถียรปรับตัวได้และทำซ้ำซึ่งต้องการการเปรียบเทียบน้อยกว่า n log (n) มากเมื่อทำงานบนอาร์เรย์ที่เรียงลำดับบางส่วนในขณะที่ให้ประสิทธิภาพเทียบเท่ากับการผสานแบบดั้งเดิมเมื่อรันบนอาร์เรย์แบบสุ่ม เช่นเดียวกับ timsort การผสานที่เหมาะสมทั้งหมดมีเสถียรภาพและทำงานในเวลา O (n log n) (กรณีที่เลวร้ายที่สุด) ในกรณีที่แย่ที่สุด timsort ต้องการพื้นที่จัดเก็บชั่วคราวสำหรับการอ้างอิงอ็อบเจ็กต์ n / 2 ในกรณีที่ดีที่สุดต้องใช้พื้นที่คงที่เพียงเล็กน้อย ตรงกันข้ามกับการใช้งานปัจจุบันซึ่งต้องใช้พื้นที่พิเศษสำหรับการอ้างอิงอ็อบเจ็กต์ n เสมอและเต้น n log n เฉพาะในรายการที่เรียงลำดับเกือบเท่านั้น

Timsort อธิบายไว้ในรายละเอียดที่นี่: http://svn.python.org/projects/python/trunk/Objects/listsort.txt

การนำไปใช้งานดั้งเดิมของ Tim Peters เขียนด้วยภาษา C Joshua Bloch ได้ย้ายจาก C ไปยัง Java และสิ้นสุดการทดสอบเปรียบเทียบและปรับแต่งโค้ดผลลัพธ์อย่างครอบคลุม โค้ดผลลัพธ์คือการแทนที่แบบดรอปอินสำหรับ java.util.Arrays.sort ในข้อมูลที่มีลำดับสูงรหัสนี้สามารถทำงานได้เร็วกว่าการใช้งานปัจจุบันถึง 25 เท่า (บนเซิร์ฟเวอร์ HotSpot VM) ในข้อมูลแบบสุ่มความเร็วของการใช้งานเก่าและใหม่จะเทียบเคียง สำหรับรายการที่สั้นมากการใช้งานใหม่จะเร็วกว่าอย่างมากแม้ว่าข้อมูลเก่าจะเป็นแบบสุ่ม (เนื่องจากหลีกเลี่ยงการคัดลอกข้อมูลที่ไม่จำเป็น)

นอกจากนี้โปรดดูที่Java 7 ใช้ Tim Sort สำหรับ Method Arrays.Sort หรือไม่ .

ไม่มีทางเลือกเดียวที่ "ดีที่สุด" เช่นเดียวกับสิ่งอื่น ๆ อีกมากมายมันเกี่ยวกับการแลกเปลี่ยน

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