เหตุใด Quicksort จึงดีกว่าการรวม


354

ฉันถูกถามคำถามนี้ระหว่างการสัมภาษณ์ พวกเขาทั้ง O (nlogn) และยังคนส่วนใหญ่ใช้ Quicksort แทน Mergesort ทำไมถึงเป็นอย่างนั้น?


91
นี่ไม่ใช่คำถามสัมภาษณ์ที่ดีมาก ข้อมูลในโลกแห่งความเป็นจริงไม่ได้ถูกสับเปลี่ยน: บ่อยครั้งมีการเรียงลำดับจำนวนมากซึ่งการจัดเรียงอัจฉริยะสามารถใช้ประโยชน์ได้และในขณะที่อัลกอริธึมไม่ทำสิ่งนี้โดยอัตโนมัติ GNU libc's qsort, Python list.sort, และArray.prototype.sortJavaScript ของ Firefox นั้นทุกอย่างผสานเข้าด้วยกัน (GNU STL sortใช้ Introsort แทน แต่ที่อาจจะเป็นเพราะใน C ++ แลกเปลี่ยนอาจชนะที่ยิ่งใหญ่กว่าการคัดลอก.)
เจสัน Orendorff

3
@ Jason Orendorff: ทำไม"easier to hack a mergesort to do it than a quicksort"ล่ะ ตัวอย่างเฉพาะใด ๆ ที่คุณสามารถอ้างอิงได้?
Lazer

16
@eSKay การเรียงลำดับการผสานเริ่มต้นด้วยการจัดกลุ่มข้อมูลเริ่มต้นลงใน subarrays ที่เรียงลำดับ หากเริ่มต้นอาร์เรย์มีพื้นที่ที่มีการเรียงลำดับแล้วบางส่วนคุณสามารถประหยัดเวลาได้มากเพียงแค่ตรวจสอบว่าพวกมันอยู่ที่นั่นก่อนที่จะเริ่ม และคุณสามารถทำได้ในเวลา O (n) สำหรับตัวอย่างเฉพาะดูซอร์สโค้ดของสามโครงการที่ฉันพูดถึง! ตัวอย่างที่ดีที่สุดอาจจะเป็นงูใหญ่ Timsort อธิบายในรายละเอียดที่นี่: svn.python.org/view/python/trunk/Objects/...และดำเนินการในsvn.python.org/view/python/trunk/Objects/...
Jason Orendorff

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

3
@j_random_hacker: ใช่นั่นคือสิ่งที่ฉันพูดถึง แต่ให้พิจารณา: {10, 2, 3, 4, 5, 6, 7, 8, 1, 9} แม้จะมีการเรียงลำดับเกือบสมบูรณ์แล้วการตรวจสอบก่อนพาร์ติชันจะไม่พบหรือหลังจากนั้น และพาร์ติชันจะขันสกรูขึ้นก่อนที่การโทรที่ตามมาจะตรวจสอบ ในขณะเดียวกันการเรียงลำดับการรวมตรวจสอบลำดับการเรียงในขั้นตอนการหารก่อนที่จะถูกย้ายและคนฉลาดจะมองหาวิ่งเช่นนี้โดยเฉพาะในระหว่างขั้นตอนการหาร (ดู: เรียงลำดับทิม)
Mooing Duck

คำตอบ:


275

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

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

นอกจากนี้มันง่ายมากที่จะหลีกเลี่ยงเวลารันไทม์ที่เลวร้ายที่สุดของ Quicksort ของ O ( n 2 ) เกือบทั้งหมดโดยใช้ทางเลือกที่เหมาะสมของเดือย - เช่นเลือกโดยการสุ่ม (นี่เป็นกลยุทธ์ที่ยอดเยี่ยม)

ในทางปฏิบัติการใช้งานที่ทันสมัยจำนวนมากของ quicksort (โดยเฉพาะอย่างยิ่ง libstdc ++ std::sort) เป็นจริงการใคร่ครวญซึ่งทฤษฎีกรณีที่เลวร้ายที่สุดคือ O ( n log n ) เช่นเดียวกับการเรียงผสาน มันประสบความสำเร็จด้วยการ จำกัด ระดับความลึก recursion และเปลี่ยนไปใช้ขั้นตอนวิธีการที่แตกต่างกัน ( heapsort ) เมื่อมันเกินบันทึกn


4
บทความ Wikipedia ระบุว่ามันสลับไปเป็น heapsort ไม่ใช่ผสาน ... แค่ FYI
Sev

3
@Sev: …เช่นเดียวกับกระดาษต้นฉบับ ขอบคุณสำหรับการชี้ให้เห็นข้อผิดพลาด - ไม่ใช่ว่ามันสำคัญจริงๆเพราะเวลาในการทำงานเชิงซีมโทติคเหมือนกัน
Konrad Rudolph

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

16
@ p1 เป็นคำถามที่ดี คำตอบที่แท้จริงคือโดยเฉลี่ยแล้วสำหรับข้อมูลเฉลี่ย quicksort นั้นเร็วกว่าการรวมแบบเรียงซ้อน (และ heap sort สำหรับเรื่องนั้น) และแม้ว่ากรณีที่แย่ที่สุดของ quicksort จะช้ากว่าการรวมแบบเรียงกรณีที่แย่ที่สุดนี้สามารถบรรเทาได้อย่างง่ายดาย (ดังนั้นคำตอบของฉัน)
Konrad Rudolph

4
Quicksort จะดีกว่าในแง่ของหน่วยความจำเช่นกัน
Shashwat

287

ดังที่หลายคนสังเกตเห็นว่าประสิทธิภาพของ case สำหรับ quicksort นั้นเร็วกว่าการผสาน แต่นี่เป็นเรื่องจริงหากคุณสมมติว่าเวลาคงที่ในการเข้าถึงหน่วยความจำที่ต้องการ

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

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

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

มีหลายครั้งที่การทำความเข้าใจกับวิธีการหลีกเลี่ยงการค้นหาดิสก์ทำให้ฉันต้องใช้เวลาในการประมวลผลข้อมูลมากกว่าชั่วโมงหรือหลายสัปดาห์


1
เยี่ยมมาก ๆ ไม่ได้คิดเกี่ยวกับสมมติฐานที่ทำขึ้นเพื่อเข้าถึงโครงสร้างข้อมูล ข้อมูลเชิงลึกที่ดี :)
chutsu

2
คุณสามารถอธิบายสิ่งที่คุณหมายถึงโดย "พยายามที่จะดิสก์" มันหมายถึงการค้นหาค่าเดียวเมื่อข้อมูลถูกเก็บไว้ในดิสก์?
James Wierzba

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

1
บางคนสามารถอธิบายสิ่งนี้อีกเล็กน้อยได้ไหม? นี่คือสิ่งที่ฉันเห็น: Quicksort: ถ้าเราไปด้วย pivot แบบสุ่ม call stack จะมีแฟรกเมนต์ของการแบ่งพาร์ติชั่นในแบบสุ่ม ต้องใช้การเข้าถึงแบบสุ่ม อย่างไรก็ตามสำหรับการโทรแต่ละครั้งในสแต็กทั้งพอยน์เตอร์ซ้ายและขวาจะย้ายตามลำดับ ฉันสมมติว่าสิ่งเหล่านี้จะถูกเก็บไว้ในแคช การสลับคือการดำเนินการอีกครั้งกับข้อมูลที่อยู่ในแคช (และในที่สุดก็เขียนลงดิสก์) (ยังคงอยู่ในความคิดเห็นต่อไปของฉัน)
sam

1
มีเพียงการสนับสนุนที่หลีกเลี่ยงค่าใช้จ่ายในการอ่าน / เขียนบนแผ่นดิสก์ที่มีราคาแพง : เมื่อเรียงลำดับข้อมูลขนาดใหญ่มากซึ่งต้องการการเข้าถึงดิสก์ นั่นคือที่ระดับบนสุดของห่วงเมื่อคุณไปจาก0ต่อnและครั้งต่อไปที่คุณไปจากที่มีต่อn 0วิธีนี้จะทำให้ข้อดีของการถอยกลับ (การเรียงลำดับ) บล็อกข้อมูลที่มีอยู่แล้วในหน่วยความจำ (แคช) และการโจมตีสองครั้งสำหรับการเข้าถึงดิสก์เพียงครั้งเดียว ฉันคิดว่า DBMS ส่วนใหญ่ใช้เทคนิคการเพิ่มประสิทธิภาพนี้
ssd

89

ที่จริงแล้ว QuickSort คือ O (n 2 ) ใช้กรณีเฉลี่ยเวลาในการทำงานเป็น O (NLog (N)) แต่กรณีเลวร้ายที่สุดคือ O (n 2 ) ซึ่งเกิดขึ้นเมื่อคุณใช้มันในรายการว่ามีรายการที่ไม่ซ้ำกันไม่กี่ การสุ่มใช้เวลา O (n) แน่นอนว่านี่ไม่ใช่การเปลี่ยนแปลงกรณีที่เลวร้ายที่สุด แต่เพียงป้องกันผู้ใช้ที่ประสงค์ร้ายไม่ให้ทำการจัดเรียงของคุณใช้เวลานาน

QuickSort ได้รับความนิยมมากขึ้นเนื่องจาก:

  1. อยู่ในสถานที่ (MergeSort ต้องการหน่วยความจำเชิงเส้นเพิ่มเติมตามจำนวนขององค์ประกอบที่จะเรียงลำดับ)
  2. มีค่าคงตัวที่ซ่อนอยู่เล็กน้อย

4
ที่จริงแล้วมีการใช้งาน QuickSort ซึ่งเป็น O (n * log (n)) ไม่ใช่ O (n ^ 2) ในกรณีที่เลวร้ายที่สุด
jfs

12
นอกจากนี้ยังขึ้นอยู่กับสถาปัตยกรรมคอมพิวเตอร์ ประโยชน์ Quicksort จากแคชในขณะที่ MergeSort ไม่ได้
Cristian Ciupitu

4
@JF Sebastian: สิ่งเหล่านี้อาจเป็นการใช้งาน introsort ไม่ใช่ quicksort (introsort เริ่มต้นเป็น quicksort และเปลี่ยนเป็น heapsort หากกำลังจะหยุดเป็น n * log (n)
CesarB

44
คุณสามารถนำการรวมกิจการมาใช้แทนได้
Marcin

6
การเรียงลำดับการผสานอาจนำมาใช้ในวิธีที่ต้องการเพียง O (1) หน่วยเก็บข้อมูลเพิ่มเติม แต่การนำไปใช้ส่วนใหญ่นั้นประสบปัญหาอย่างมากในแง่ของประสิทธิภาพ
Clear

29

"แต่คนส่วนใหญ่ใช้ Quicksort แทนที่จะเป็น Mergesort ทำไมถึงเป็นเช่นนั้น"

เหตุผลทางจิตวิทยาอย่างหนึ่งที่ไม่ได้รับก็คือ Quicksort นั้นตั้งชื่ออย่างชาญฉลาดมากขึ้น เช่นการตลาดที่ดี

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


3
ไม่ตอบคำถามเกี่ยวกับสิ่งที่ดีกว่า ชื่อของอัลกอริทึมนั้นไม่เกี่ยวข้องในการพิจารณาว่าอันไหนดีกว่า
Nick Gallimore

18

ดังที่คนอื่น ๆ ได้กล่าวไว้กรณีที่แย่ที่สุดของ Quicksort คือ O (n ^ 2) ในขณะที่การรวมและ heapsort จะอยู่ที่ O (nlogn) อย่างไรก็ตามโดยเฉลี่ยแล้วทั้งสามกรณีเป็น O (nlogn) ดังนั้นจึงเป็นกรณีส่วนใหญ่เทียบเคียง

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


9

ฉันต้องการเพิ่มที่สาม algoritms ที่กล่าวถึงแล้ว (การผสานการรวมแบบเร็วและเรียงลำดับฮีป) การผสานเพียงอย่างเดียวนั้นมีเสถียรภาพ นั่นคือลำดับจะไม่เปลี่ยนแปลงสำหรับค่าเหล่านั้นที่มีคีย์เดียวกัน ในบางกรณีนี่เป็นที่พึงปรารถนา

แต่ความจริงจะบอกว่าในสถานการณ์จริงคนส่วนใหญ่ต้องการเพียงประสิทธิภาพที่ดีโดยเฉลี่ยและ quicksort คือ ... quick =)

อัลกอริทึมการเรียงลำดับทั้งหมดมีอัพและดาวน์ของพวกเขา ดูบทความ Wikipedia สำหรับการจัดเรียงอัลกอริทึมสำหรับภาพรวมที่ดี


7

จากรายการ Wikipedia บน Quicksort :

Quicksort ยังแข่งขันกับการผสานรวม, อัลกอริทึมการเรียงลำดับแบบเรียกซ้ำ แต่ด้วยประโยชน์ของเวลาที่เลวร้ายที่สุด case (nlogn) Mergesort เป็นระบบจัดเรียงที่มีความเสถียรซึ่งแตกต่างจาก quicksort และ heapsort และสามารถปรับเปลี่ยนได้อย่างง่ายดายเพื่อใช้งานในรายการที่เชื่อมโยงและรายการขนาดใหญ่มากที่เก็บไว้ในสื่อที่เข้าถึงได้ช้าเช่นที่เก็บดิสก์หรือที่เก็บข้อมูลเครือข่าย แม้ว่า quicksort สามารถเขียนเพื่อใช้งานในรายการที่เชื่อมโยงได้ แต่บ่อยครั้งจะประสบกับตัวเลือกเดือยที่ไม่ดีโดยไม่ต้องเข้าถึงแบบสุ่ม ข้อเสียเปรียบหลักของการรวมกันคือเมื่อใช้งานบนอาร์เรย์มันต้องใช้พื้นที่เสริมΘ (n) ในกรณีที่ดีที่สุดในขณะที่ตัวแปรของ quicksort ที่มีการแบ่งพาร์ติชันและการเรียกใช้หางซ้ำใช้พื้นที่Θ (logn) เท่านั้น (โปรดทราบว่าเมื่อดำเนินการกับรายการที่เชื่อมโยงการรวมกันจะต้องมีที่เก็บข้อมูลสำรองจำนวนเล็กน้อยเท่านั้น)


7

หมู่! Quicksort นั้นไม่ดีกว่ามันเหมาะสำหรับการใช้งานประเภทอื่นมากกว่าการรวม

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

คุณระบุว่าพวกเขา«พวกเขาทั้งคู่ O (nlogn) […] » นี่เป็นสิ่งที่ผิด « Quicksort ใช้การเปรียบเทียบ n ^ 2/2 ในกรณีที่เลวร้ายที่สุด» 1 .

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

1 Sedgewick อัลกอริทึม


การควบรวมกิจการสามารถดำเนินการในสถานที่เช่นที่มันไม่ต้องการพื้นที่เพิ่มเติม ตัวอย่างเช่นกับรายการที่เชื่อมโยงสองรายการ: stackoverflow.com/questions/2938495/…
lanoxx

6

Quicksort เป็นอัลกอริทึมการเรียงลำดับที่เร็วที่สุดในทางปฏิบัติ แต่มีจำนวนกรณีทางพยาธิวิทยาที่สามารถทำให้มันทำงานได้ไม่ดีเท่า O (n2)

Heapsort รับประกันว่าจะทำงานใน O (n * ln (n)) และต้องการพื้นที่เก็บข้อมูลเพิ่มเติมที่ จำกัด เท่านั้น แต่มีการอ้างอิงจำนวนมากของการทดสอบในโลกแห่งความเป็นจริงซึ่งแสดงว่า heapsort ช้ากว่าการจัดเรียงโดยเฉลี่ยอย่างรวดเร็ว


5

คำอธิบายของ Wikipedia คือ:

โดยทั่วไปแล้ว quicksort จะเร็วกว่าในทางปฏิบัติมากกว่าอัลกอริธึมΘ (nlogn) อื่น ๆ เนื่องจาก loop ภายในสามารถนำไปใช้กับสถาปัตยกรรมส่วนใหญ่ได้อย่างมีประสิทธิภาพและในโลกแห่งความเป็นจริงก็เป็นไปได้ที่จะเลือกตัวเลือกการออกแบบ .

quicksort

mergesort

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


กรณีที่แย่ที่สุดของ quicksort คือ O (n), ผสาน O (n log n) - ดังนั้นจึงมีความแตกต่างใหญ่
paul23

1
กรณีที่เลวร้ายที่สุด quicksort คือ O (n ^ 2) - ไม่สามารถแก้ไขความคิดเห็นก่อนหน้าของฉันและทำผิดพลาดได้
paul23

@ paul23 ความคิดเห็นสามารถลบได้ นอกจากนี้คำตอบได้กล่าวถึงประเด็นของคุณแล้วว่า: "ในโลกแห่งความเป็นจริงข้อมูลส่วนใหญ่มีความเป็นไปได้ที่จะเลือกตัวเลือกที่ช่วยลดความน่าจะเป็นที่จะต้องใช้เวลากำลังสอง"
Jim Balter

5

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

นอกเหนือจากปัญหาการเข้าถึงแบบสุ่มมีสองปัจจัยหลักที่สามารถส่งผลกระทบต่อประสิทธิภาพการทำงานของ QuickSort และพวกเขาทั้งสองเกี่ยวข้องกับวิธีที่เดือยเปรียบเทียบกับข้อมูลที่ถูกเรียงลำดับ

1) คีย์จำนวนน้อยในข้อมูล ชุดข้อมูลที่มีค่าเดียวกันทั้งหมดจะเรียงลำดับใน n ^ 2 ครั้งบน QuickSort วานิลลาพาร์ติชัน 2 เนื่องจากค่าทั้งหมดยกเว้นตำแหน่ง pivot จะถูกวางไว้ที่ด้านหนึ่งในแต่ละครั้ง การใช้งานที่ทันสมัยอยู่นี้โดยวิธีการเช่นการใช้การเรียงลำดับ 3 พาร์ติชัน วิธีการเหล่านี้ดำเนินการในชุดข้อมูลที่มีค่าเดียวกันทั้งหมดในเวลา O (n) ดังนั้นการใช้การติดตั้งแบบนี้หมายความว่าอินพุตที่มีคีย์จำนวนน้อยจะช่วยเพิ่มประสิทธิภาพการทำงานได้จริงและไม่ต้องกังวลอีกต่อไป

2) การเลือกเดือยที่แย่มากอาจทำให้ประสิทธิภาพของเคสแย่ที่สุด ในกรณีที่เหมาะสาระสำคัญเดือยจะเป็นเช่นนั้นเสมอ 50% ข้อมูลมีขนาดเล็กและ 50% ข้อมูลใหญ่กว่าดังนั้นข้อมูลจะถูกแบ่งครึ่งในระหว่างการทำซ้ำแต่ละครั้ง สิ่งนี้ทำให้เราสามารถเปรียบเทียบและสลับเวลาได้แบบ log-2 (n) ซ้ำสำหรับเวลา O (n * logn)

การเลือกเดือยที่ไม่เหมาะจะส่งผลต่อเวลาดำเนินการเท่าไหร่

ลองพิจารณากรณีที่มีการเลือกเดือยอย่างสม่ำเสมอซึ่ง 75% ของข้อมูลอยู่ด้านหนึ่งของเดือย มันยังคงเป็น O (n * logn) แต่ตอนนี้ฐานของบันทึกได้เปลี่ยนเป็น 1 / 0.75 หรือ 1.33 ความสัมพันธ์ในการปฏิบัติงานเมื่อเปลี่ยนฐานเป็นค่าคงที่ที่แสดงโดย log (2) / log (newBase) เสมอ ในกรณีนี้ค่าคงที่นั้นคือ 2.4 ดังนั้นคุณภาพของตัวเลือกแบบเดือยจึงใช้เวลานานกว่าอุดมคติ

สิ่งนี้จะเลวร้ายยิ่งเร็วแค่ไหน?

ไม่เร็วมากจนกระทั่งตัวเลือก pivot ได้รับ (สม่ำเสมอ) แย่มาก:

  • 50% ในด้านเดียว: (กรณีที่เหมาะ)
  • 75% ในด้านเดียว: 2.4 เท่า
  • 90% ในด้านเดียว: 6.6 เท่า
  • 95% ในด้านเดียว: 13.5 เท่านาน
  • 99% ในด้านเดียว: 69 ครั้ง

ในขณะที่เราเข้าใกล้ 100% ในด้านหนึ่งส่วนบันทึกของการดำเนินการเข้าใกล้ n และการดำเนินการทั้งหมดเข้าใกล้ O (n ^ 2)

ในการใช้งาน QuickSort ที่ไร้เดียงสากรณีต่างๆเช่นอาร์เรย์ที่เรียงลำดับ (สำหรับเดือยองค์ประกอบที่ 1) หรืออาร์เรย์ที่เรียงกลับกัน (สำหรับเดือยองค์ประกอบสุดท้าย) จะสร้างเวลาดำเนินการที่เลวร้ายที่สุด O (n ^ 2) นอกจากนี้การใช้งานกับการเลือกเดือยที่คาดการณ์ได้อาจถูกโจมตีจาก DoS ด้วยข้อมูลที่ออกแบบมาเพื่อสร้างการประมวลผลกรณีที่เลวร้ายที่สุด การใช้งานที่ทันสมัยหลีกเลี่ยงปัญหานี้ด้วยวิธีการที่หลากหลายเช่นการสุ่มข้อมูลก่อนการเรียงลำดับการเลือกค่ามัธยฐานของ 3 ดัชนีที่เลือกแบบสุ่ม ฯลฯ ด้วยการสุ่มในการผสมนี้เรามี 2 กรณี:

  • ชุดข้อมูลขนาดเล็ก กรณีที่เลวร้ายที่สุดเป็นไปได้อย่างสมเหตุสมผล แต่ O (n ^ 2) ไม่ได้เป็นหายนะเพราะ n มีขนาดเล็กพอที่ n ^ 2 ก็มีขนาดเล็กเช่นกัน
  • ชุดข้อมูลขนาดใหญ่ กรณีที่เลวร้ายที่สุดเป็นไปได้ในทางทฤษฎี แต่ไม่ใช่ในทางปฏิบัติ

มีโอกาสมากที่เราจะเห็นประสิทธิภาพแย่มาก?

โอกาสมีขนาดเล็กเต็มที ลองพิจารณาประเภท 5,000 ค่า:

การใช้สมมุติฐานของเราจะเลือกเดือยโดยใช้ค่ามัธยฐานของ 3 ดัชนีที่เลือกแบบสุ่ม เราจะพิจารณา pivots ที่อยู่ในช่วง 25% -75% ให้เป็น "ดี" และ pivots ที่อยู่ในช่วง 0% -25% หรือ 75% -100% เป็น "ไม่ดี" ถ้าคุณดูการแจกแจงความน่าจะเป็นโดยใช้ค่ามัธยฐานของดัชนีสุ่ม 3 ค่าการเรียกซ้ำแต่ละครั้งจะมีโอกาส 11/16 ในการจบด้วยเดือยที่ดี ให้เราตั้งสมมติฐานที่อนุรักษ์นิยม (และเท็จ) 2 ข้อเพื่อทำให้คณิตศาสตร์ง่ายขึ้น:

  1. pivots ที่ดีจะอยู่ที่ 25% / 75% เสมอและใช้งานได้ในกรณีที่เหมาะสมที่สุด 2.4 * เราไม่เคยได้รับการแยกที่สมบูรณ์แบบหรือการแยกใด ๆ ที่ดีกว่า 25/75

  2. pivots ที่ไม่ดีมักเป็นกรณีที่เลวร้ายที่สุดและไม่ได้มีส่วนช่วยอะไรเลย

การใช้งาน QuickSort ของเราจะหยุดที่ n = 10 และเปลี่ยนเป็นการเรียงลำดับการแทรกดังนั้นเราจึงต้องการพาร์ทิชันเดือย 22 25% / 75% เพื่อแบ่งอินพุต 5,000 ค่าจากจุดนั้น (10 * 1.333333 ^ 22> 5,000) หรือเราต้องการ pivots 4990 กรณีที่แย่ที่สุด โปรดจำไว้ว่าถ้าเราสะสม 22 เดือยที่ดี ณจุดใดการเรียงลำดับจะเสร็จสมบูรณ์ดังนั้นกรณีที่เลวร้ายที่สุดหรืออะไรก็ตามที่อยู่ใกล้มันต้องมีโชคร้ายมาก ถ้าเราใช้การเรียกซ้ำ 88 ครั้งเพื่อให้ได้ pivots 22 ตัวที่จำเป็นในการเรียงลำดับลงไปที่ n = 10 นั่นจะเป็นกรณีอุดมคติ 4 * 2.4 * หรือประมาณ 10 เท่าของเวลาดำเนินการของคดีอุดมคติ มีโอกาสเป็นไปได้มากน้อยเพียงใดที่เราจะไม่ได้รับ 22 เดือยที่ดีหลังจากการเรียกซ้ำ 88 ครั้ง

การแจกแจงความน่าจะเป็นแบบทวินามสามารถตอบได้และคำตอบคือประมาณ 10 ^ -18 (n คือ 88, k คือ 21, p คือ 0.6875) ผู้ใช้ของคุณมีแนวโน้มที่จะถูกฟ้าผ่าในเวลา 1 วินาทีที่จะคลิก [SORT] มากกว่าที่จะเห็นว่า 5,000 รายการเรียงลำดับที่แย่กว่านี้มากกว่า 10 * กรณีที่เหมาะ โอกาสนี้น้อยลงเมื่อชุดข้อมูลมีขนาดใหญ่ขึ้น นี่คือขนาดอาเรย์บางส่วนและโอกาสที่สอดคล้องกันในการรันนานกว่า 10 * ในอุดมคติ:

  • Array of 640 ไอเท็ม: 10 ^ -13 (ต้องการเดือยดี 15 แต้มจากการลอง 60 ครั้ง)
  • อาร์เรย์ 5,000 รายการ: 10 ^ -18 (ต้องใช้ 22 pivots ที่ดีจาก 88 ครั้ง)
  • Array of 40,000 รายการ: 10 ^ -23 (ต้องใช้ 29 ดี pivots จาก 116)

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

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


1
"คำตอบที่ยอดเยี่ยมที่มีอยู่" - อันไหนคือ? ฉันหาพวกมันไม่เจอ
Jim Balter

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

4

ทำไม Quicksort ถึงดี?

  • QuickSort ใช้ N ^ 2 ในกรณีที่เลวร้ายที่สุดและกรณีเฉลี่ย NlogN กรณีที่เลวร้ายที่สุดเกิดขึ้นเมื่อมีการเรียงลำดับข้อมูล สิ่งนี้สามารถบรรเทาได้โดยการสุ่มแบบสุ่มก่อนที่จะเริ่มการเรียงลำดับ
  • QuickSort ไม่ได้ใช้หน่วยความจำเพิ่มเติมที่ถูกจัดเรียงตามการผสาน
  • หากชุดข้อมูลมีขนาดใหญ่และมีรายการเหมือนกันความซับซ้อนของ Quicksort จะลดลงโดยใช้พาร์ติชัน 3 ทาง ยิ่งไม่มีรายการที่เหมือนกันจะเรียงลำดับได้ดีกว่า หากรายการทั้งหมดเหมือนกันมันจะเรียงลำดับในเวลาเชิงเส้น [นี่เป็นการใช้งานเริ่มต้นในห้องสมุดส่วนใหญ่]

Quicksort ดีกว่า Mergesort เสมอหรือไม่

ไม่ได้จริงๆ

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

บันทึก:ใน java ฟังก์ชัน Arrays.sort () ใช้ Quicksort สำหรับชนิดข้อมูลดั้งเดิมและ Mergesort สำหรับชนิดข้อมูลวัตถุ เนื่องจากวัตถุใช้หน่วยความจำโอเวอร์เฮดดังนั้นการเพิ่มโอเวอร์เฮดเล็กน้อยสำหรับการรวมอาจไม่มีปัญหาใด ๆ สำหรับมุมมองประสิทธิภาพ

การอ้างอิง : ดูวิดีโอ QuickSort ของสัปดาห์ที่ 3 หลักสูตร Princeton Algorithms ที่ Coursera


"สิ่งนี้สามารถลดลงได้โดยการสุ่มแบบสุ่มก่อนที่จะเริ่มต้นการเรียงลำดับ" - เอ่อไม่ว่าจะมีราคาแพง ใช้ pivots แบบสุ่มแทน
Jim Balter

4

Quicksort นั้นไม่ดีไปกว่าการรวมกัน ด้วย O (n ^ 2) (กรณีที่เลวร้ายที่สุดที่ไม่ค่อยเกิดขึ้น) quicksort อาจช้ากว่า O (nlogn) ของการเรียงรวม Quicksort มีค่าใช้จ่ายน้อยลงดังนั้นเมื่อใช้คอมพิวเตอร์ขนาดเล็กและช้าจะดีกว่า แต่คอมพิวเตอร์มีความรวดเร็วในวันนี้จนทำให้ค่าใช้จ่ายในการรวมกิจการเพิ่มขึ้นเล็กน้อยและความเสี่ยงของการทำธุรกรรมที่ช้ามากช้ากว่าค่าใช้จ่ายที่ไม่สำคัญของการรวมกิจการในกรณีส่วนใหญ่

นอกจากนี้การรวมกันยังทำให้รายการมีคีย์ที่เหมือนกันในลำดับเดิมซึ่งเป็นแอตทริบิวต์ที่มีประโยชน์


2
ประโยคที่สองของคุณบอกว่า "... การผสานอาจช้ากว่า ... การรวม" การอ้างอิงครั้งแรกน่าจะเป็นการด่วน
Jonathan Leffler

การเรียงลำดับผสานนั้นมีความเสถียรหากอัลกอริทึมการผสานนั้นเสถียร สิ่งนี้ไม่รับประกัน
Clear

@Clearer มันรับประกันว่า<=จะใช้สำหรับการเปรียบเทียบมากกว่า<และไม่มีเหตุผลที่จะไม่
Jim Balter

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

@Clearer quicksort ไม่ใช่การรวมการผสาน ... คำสั่ง 21 ธ.ค. '14 ของคุณที่ฉันตอบไปนั้นเคร่งครัดกับการผสานการเรียงลำดับและไม่ว่าจะมีเสถียรภาพหรือไม่ quicksort และเร็วกว่านั้นไม่เกี่ยวข้องกับความคิดเห็นของคุณหรือคำตอบของฉัน จบการสนทนาสำหรับฉัน ... ซ้ำแล้วซ้ำอีก
Jim Balter

3

คำตอบจะเอียงไปทาง quicksort wrt เล็กน้อยต่อการเปลี่ยนแปลงที่เกิดขึ้นด้วย DualPivotQuickSort สำหรับค่าดั้งเดิม มันถูกใช้ในJAVA 7เพื่อเรียงลำดับในjava.util.Arrays

It is proved that for the Dual-Pivot Quicksort the average number of
comparisons is 2*n*ln(n), the average number of swaps is 0.8*n*ln(n),
whereas classical Quicksort algorithm has 2*n*ln(n) and 1*n*ln(n)
respectively. Full mathematical proof see in attached proof.txt
and proof_add.txt files. Theoretical results are also confirmed
by experimental counting of the operations.

คุณสามารถค้นหาความหมายของ JAVA7 ได้ที่นี่ - http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/Arrays.java

การอ่านที่ยอดเยี่ยมเพิ่มเติมเกี่ยวกับ DualPivotQuickSort - http://permalink.gmane.org/gmane.comp.java.openjdk.core-libs.devel/2628


3

ในการจัดเรียงผสานอัลกอริทึมทั่วไปคือ:

  1. จัดเรียงอาร์เรย์ย่อยทางซ้าย
  2. จัดเรียงอาร์เรย์ย่อยที่ถูกต้อง
  3. ผสาน 2 อาร์เรย์ย่อยเรียง

ที่ระดับบนสุดการรวม 2 อาร์เรย์ย่อยที่เรียงลำดับเกี่ยวข้องกับการจัดการกับองค์ประกอบ N

หนึ่งระดับที่ต่ำกว่านั้นการวนซ้ำของขั้นตอนที่ 3 เกี่ยวข้องกับการจัดการกับองค์ประกอบ N / 2 แต่คุณต้องทำซ้ำกระบวนการนี้สองครั้ง ดังนั้นคุณยังคงต้องรับมือกับองค์ประกอบ 2 * N / 2 == N

หนึ่งระดับที่ต่ำกว่านั้นคุณกำลังรวมองค์ประกอบ 4 * N / 4 == N องค์ประกอบและอื่น ๆ ความลึกทุกครั้งในสแต็กแบบเรียกซ้ำเกี่ยวข้องกับการรวมจำนวนองค์ประกอบเดียวกันในทุกการโทรสำหรับความลึกนั้น

พิจารณาอัลกอริทึมการเรียงลำดับอย่างรวดเร็วแทน:

  1. เลือกจุดหมุน
  2. วางจุดหมุนในตำแหน่งที่ถูกต้องในอาร์เรย์โดยมีองค์ประกอบที่เล็กกว่าทั้งหมดไว้ทางซ้ายและองค์ประกอบที่ใหญ่กว่าอยู่ทางขวา
  3. เรียงลำดับย่อยซ้าย
  4. เรียงลำดับย่อยขวา

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

หนึ่งระดับที่ต่ำกว่านั้นคุณกำลังจัดการกับ 2 อาร์เรย์ย่อยที่มีขนาดรวมของ N-1 (เช่นลบจุดหมุนก่อนหน้านี้) คุณเลือกจุดหมุนสำหรับแต่ละแถวย่อยซึ่งมีจุดหมุนเพิ่มเติม 2 จุด

หนึ่งระดับที่ต่ำกว่านั้นคุณกำลังจัดการกับ 4 แถวย่อยที่มีขนาดรวม N-3 ด้วยเหตุผลเดียวกับข้างต้น

จากนั้น N-7 ... จากนั้น N-15 ... จากนั้น N-32 ...

ความลึกของสแต็กแบบวนซ้ำของคุณยังคงอยู่โดยประมาณ (logN) ด้วยการผสานการจัดเรียงคุณจะต้องจัดการกับการรวมองค์ประกอบ N ตลอดทุกระดับของสแต็กแบบเรียกซ้ำ ด้วยการจัดเรียงอย่างรวดเร็วจำนวนขององค์ประกอบที่คุณจัดการกับลดลงเมื่อคุณลงไปในกอง ตัวอย่างเช่นถ้าคุณดูที่ความลึกตรงกลางผ่านสแต็กแบบเรียกซ้ำจำนวนขององค์ประกอบที่คุณติดต่อด้วยคือ N - 2 ^ ((logN) / 2)) == N - sqrt (N)

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


การที่ pivots ไม่ได้เป็นส่วนหนึ่งของระดับถัดไปไม่ใช่เหตุผลว่าทำไม QS ถึงมีประสิทธิภาพมากกว่า ดูคำตอบอื่น ๆ สำหรับข้อมูลเชิงลึกเพิ่มเติม
Jim Balter

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

คุณกำลังย้ายเสาประตูจากสาเหตุที่ QS มีประสิทธิภาพมากกว่าเพื่ออธิบายข้อเท็จจริงพื้นฐานเกี่ยวกับวิธีการทำงานของมัน คำตอบสำหรับคำถามอื่น ๆ ทำเช่นนั้น: stackoverflow.com/questions/9444714/… ... ฉันหวังว่าจะเพียงพอสำหรับคุณ ฉันจะไม่ตอบสนองต่อไป
Jim Balter

3

ไม่เหมือนกับ Merge Sort Quick Sort ไม่ได้ใช้พื้นที่เสริม โดยที่ Merge Sort ใช้ช่องว่างเสริม O (n) แต่ Merge Sort มีความซับซ้อนของเวลากรณีที่เลวร้ายที่สุดของ O (nlogn) ในขณะที่ความซับซ้อนของกรณีที่แย่ที่สุดของ Quick Sort คือ O (n ^ 2) ซึ่งเกิดขึ้นเมื่ออาร์เรย์เรียงลำดับแล้ว


ไม่กรณีที่เลวร้ายที่สุดของ QuickSort จะไม่เกิดขึ้นเมื่อมีการเรียงลำดับอาร์เรย์ไว้แล้วยกเว้นว่าคุณใช้รายการแรกหรือรายการสุดท้ายเป็นเดือย แต่ไม่มีใครทำเช่นนั้น
Jim Balter

2

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

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

ฉันเป็นแฟนตัวยงของการควบรวมกิจการมากกว่าที่ฉันเป็น Quicksort ด้วยเหตุผลเหล่านี้


2
Quicksort มีความซับซ้อนของขนาดตัวพิมพ์โดยเฉลี่ยอย่างไรดีกว่า พวกเขาเป็นทั้ง O (nlgn) ฉันจะยืนยันว่าผู้โจมตีจะไม่ให้ข้อมูลกับอัลกอริทึมการเรียงลำดับใด ๆ ... แต่ในความสนใจที่จะไม่คิดว่าการรักษาความปลอดภัยโดยความคลุมเครือให้สมมติว่าเขาทำได้ ในขณะที่เวลาในการทำงาน n ^ 2 นั้นแย่กว่า nlgn แต่ก็ไม่ได้แย่ไปกว่าเว็บเซิร์ฟเวอร์ที่จะทำงานผิดพลาดจากการโจมตีเพียงครั้งเดียว ในความเป็นจริงอาร์กิวเมนต์ DOS นั้นค่อนข้างเป็นโมฆะเนื่องจากเว็บเซิร์ฟเวอร์ใด ๆ มีความเสี่ยงต่อการถูกโจมตีแบบ DDOS และมีแนวโน้มที่ผู้โจมตีจะใช้เครือข่ายแบบกระจายของโฮสต์ซึ่งเป็น TCP SYN ทั้งหมดที่เกิดน้ำท่วม
CaTalyst.X

"Quicksort มีความซับซ้อนของกรณีโดยเฉลี่ยที่ดีกว่า" - ไม่มีเลย
Jim Balter

2

มันยากที่จะบอกว่า MergeSort ที่แย่ที่สุดคือ n (log2n) -n + 1 ซึ่งถูกต้องถ้า n เท่ากับ 2 ^ k (ฉันได้พิสูจน์แล้ว) และสำหรับ n ใด ๆ มันอยู่ระหว่าง (n lg n - n + 1) และ (n lg n + n + O (lg n)) แต่สำหรับ quickSort วิธีที่ดีที่สุดคือ nlog2n (เช่น n เท่ากับ 2 ^ k) หากคุณหารการรวมกันโดย quickSort จะเท่ากับหนึ่งเมื่อ n ไม่มีที่สิ้นสุดดังนั้น มันเหมือนกับว่ากรณีที่แย่ที่สุดของ MergeSort นั้นดีกว่ากรณีที่ดีที่สุดของ QuickSort ทำไมเราถึงใช้ quicksort แต่จำไว้ว่า MergeSort ไม่ได้อยู่ในสถานที่มันต้องการพื้นที่ 2n memeroy และ MergeSort ก็ต้องทำสำเนาอาเรย์มากมาย ไม่รวมอยู่ในการวิเคราะห์อัลกอริทึมในคำ MergeSort จริงๆ faseter มากกว่า quicksort ใน theroy แต่ในความเป็นจริงคุณต้องพิจารณาพื้นที่ memeory ค่าใช้จ่ายของการคัดลอกอาร์เรย์การควบรวมกิจการจะช้ากว่าการเรียงลำดับอย่างรวดเร็ว การทดลองที่ฉันได้รับ 1000000 หลักใน java โดยชั้นเรียนแบบสุ่มและใช้เวลารวม 2610 มิลลิวินาทีโดยการรวมกัน, 1370 มิลลิวินาทีโดยการดูด


2

การเรียงลำดับด่วนเป็นกรณีที่เลวร้ายที่สุด O (n ^ 2) อย่างไรก็ตามกรณีเฉลี่ยออกมาอย่างสม่ำเสมอจะทำการเรียงลำดับการผสาน อัลกอริทึมแต่ละตัวคือ O (nlogn) แต่คุณต้องจำไว้ว่าเมื่อพูดถึง Big O เราจะปล่อยให้ปัจจัยความซับซ้อนต่ำลง การจัดเรียงอย่างรวดเร็วมีการปรับปรุงที่สำคัญมากกว่าการรวมการเรียงเมื่อมันมาถึงปัจจัยคงที่

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

ข้อมูลเสริม:

กรณีที่เลวร้ายที่สุดของการจัดเรียงอย่างรวดเร็วเกิดขึ้นเมื่อมีการเลือกเดือยไม่ดี ลองพิจารณาตัวอย่างต่อไปนี้:

[5, 4, 3, 2, 1]

หากเลือกเดือยเป็นจำนวนที่เล็กที่สุดหรือใหญ่ที่สุดในกลุ่มการจัดเรียงอย่างรวดเร็วจะทำงานใน O (n ^ 2) ความน่าจะเป็นที่จะเลือกองค์ประกอบที่มีค่ามากที่สุดหรือเล็กที่สุด 25% ของรายการคือ 0.5 นั่นทำให้อัลกอรึทึมมีโอกาส 0.5 ในการเป็นเดือยที่ดี ถ้าเราใช้อัลกอริทึมการเลือกเดือยทั่วไป (พูดว่าการเลือกองค์ประกอบแบบสุ่ม) เรามีโอกาส 0.5 ที่จะเลือกเดือยที่ดีสำหรับตัวเลือกของเดือยทุกตัว สำหรับคอลเลกชันที่มีขนาดใหญ่ความน่าจะเป็นที่จะเลือกเดือยที่ไม่ดีคือ 0.5 * n การจัดเรียงอย่างรวดเร็วน่าจะเป็นนี้มีประสิทธิภาพสำหรับกรณีเฉลี่ย (และทั่วไป)


O (2n) == O (n) คำสั่งที่ถูกต้องคือ Mergesort ต้องการหน่วยความจำเพิ่มเติม O (n) (โดยเฉพาะอย่างยิ่งต้องการหน่วยความจำเสริม n / 2) และนี่ไม่เป็นความจริงสำหรับรายการที่ลิงก์
Jim Balter

@JimBalter Sir คุณจะแบ่งปันความคิดที่ยอดเยี่ยมและคุ้มค่ากับเราเกี่ยวกับน้ำหอมของพวกเขาเป็นคำตอบของคำถามหรือไม่ ขอบคุณล่วงหน้า.
snr

2

นี่เป็นคำถามที่ค่อนข้างเก่า แต่เนื่องจากเมื่อเร็ว ๆ นี้ฉันได้รับการตอบโต้ทั้งสองข้อนี่คือ 2c ของฉัน:

การเรียงแบบผสานต้องการการเปรียบเทียบโดยเฉลี่ย ~ N บันทึก N สำหรับอาร์เรย์ที่เรียงลำดับแล้ว (เกือบ) ที่เรียงลำดับแล้วสิ่งนี้จะลดลงเหลือ 1/2 N บันทึก N เนื่องจากในขณะที่รวมเรา (เกือบ) จะเลือกส่วน "ซ้าย" เสมอ 1/2 N ครั้งแล้วคัดลอกองค์ประกอบ 1/2 N ที่ถูกต้อง นอกจากนี้ฉันสามารถคาดเดาได้ว่าอินพุตที่เรียงลำดับแล้วทำให้ตัวทำนายสาขาของโปรเซสเซอร์เปล่งประกาย แต่คาดเดาได้เกือบทุกสาขาอย่างถูกต้องจึงป้องกันการวางแผงไปป์ไลน์

การเรียงลำดับแบบด่วนโดยเฉลี่ยต้องใช้การเปรียบเทียบ ~ 1.38 N บันทึก N มันไม่ได้ประโยชน์อย่างมากจากการเรียงอาเรย์แล้วในแง่ของการเปรียบเทียบ (แต่มันทำในแง่ของการแลกเปลี่ยนและอาจเป็นในแง่ของการพยากรณ์สาขาในซีพียู)

มาตรฐานของฉันเกี่ยวกับโปรเซสเซอร์ที่ทันสมัยพอสมควรแสดงดังต่อไปนี้:

เมื่อฟังก์ชั่นการเปรียบเทียบเป็นฟังก์ชั่นการโทรกลับ (เช่นใน qsort () การใช้ libc) Quicksort จะช้ากว่าการรวม 15% จากการสุ่มอินพุตและ 30% สำหรับอาร์เรย์ที่เรียงลำดับแล้วสำหรับจำนวนเต็ม 64 บิต

ในทางกลับกันถ้าการเปรียบเทียบไม่ใช่การโทรกลับประสบการณ์ของฉันคือการที่ quicksort มีประสิทธิภาพสูงกว่าการรวมกันมากถึง 25%

อย่างไรก็ตามถ้าอาร์เรย์ (ขนาดใหญ่) ของคุณมีค่าที่ไม่ซ้ำกันมากการเรียงแบบผสานจะเริ่มได้รับการตอบสนองเร็วในทุกกรณี

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

ที่กล่าวไว้ก่อนหน้านี้ทั้งหมดบอกว่าเป็นจริง: - Quicksort สามารถเป็น N ^ 2 แต่ Sedgewick อ้างว่าการใช้งานแบบสุ่มที่ดีนั้นมีโอกาสมากขึ้นที่คอมพิวเตอร์จะทำการเรียงลำดับที่ถูกฟ้าผ่ามากกว่าที่จะไป N ^ 2 - Mergesort ต้องการพื้นที่เพิ่มเติม


qsort ชนะการรวมกันแม้สำหรับอินพุตที่เรียงลำดับถ้าการเปรียบเทียบมีราคาถูก?
Eonil

2

เมื่อฉันทดลองกับอัลกอริธึมการเรียงลำดับทั้งสองโดยการนับจำนวนการโทรซ้ำแบบเรียกซ้ำการโทรแบบเร็วจะมีการโทรซ้ำแบบเรียกซ้ำน้อยกว่าการรวมแบบซ้ำ เป็นเพราะ quicksort มี pivots และ pivots ไม่รวมอยู่ในการโทรซ้ำครั้งถัดไป วิธีนี้ Quicksort สามารถเข้าถึงกรณีพื้นฐานแบบเรียกซ้ำได้เร็วกว่าการรวมแบบซ้ำ


Pivots ไม่มีส่วนเกี่ยวข้องกับเหตุผลที่ว่าทำไม QS มีการเรียกซ้ำแบบเรียกซ้ำน้อยกว่า ... นั่นเป็นเพราะการเรียกซ้ำครึ่งหนึ่งของ QS เป็นการเรียกซ้ำแบบหางซึ่งสามารถกำจัดได้
Jim Balter

2

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

1- พื้นที่เสริม: การจัดเรียงอย่างรวดเร็วเป็นอัลกอริทึมการเรียงลำดับแบบแทนที่ การเรียงลำดับแบบ in-place หมายถึงไม่จำเป็นต้องใช้พื้นที่เก็บข้อมูลเพิ่มเติมในการเรียงลำดับ การเรียงลำดับการผสานในทางกลับกันจำเป็นต้องมีอาร์เรย์ชั่วคราวเพื่อรวมอาร์เรย์ที่เรียงแล้วดังนั้นจึงไม่ได้อยู่ในตำแหน่ง

2- กรณีที่แย่ที่สุด: กรณีที่แย่ที่สุดของ quicksort O(n^2)สามารถหลีกเลี่ยงได้โดยใช้ quicksort แบบสุ่ม สามารถหลีกเลี่ยงได้อย่างง่ายดายด้วยความน่าจะเป็นสูงโดยเลือกเดือยที่เหมาะสม การได้รับพฤติกรรมของเคสโดยเฉลี่ยโดยการเลือกองค์ประกอบเดือยที่ถูกต้องจะทำให้การทำงานนั้นมีประสิทธิภาพและมีประสิทธิภาพเท่ากับการผสานการจัดเรียง

3- ตำแหน่งของการอ้างอิง: Quicksort โดยเฉพาะจะแสดงตำแหน่งแคชที่ดีและทำให้เร็วกว่าการรวมการเรียงลำดับในหลายกรณีเช่นในสภาพแวดล้อมของหน่วยความจำเสมือน

4- การเรียกซ้ำแบบหาง: QuickSort จะเป็นการวนแบบวนซ้ำในขณะที่การเรียงแบบผสานไม่ใช่ ฟังก์ชัน recursive แบบหางเป็นฟังก์ชันที่การเรียกแบบเรียกซ้ำเป็นสิ่งสุดท้ายที่ดำเนินการโดยฟังก์ชัน ฟังก์ชั่นการเรียกซ้ำแบบหางถือว่าดีกว่าฟังก์ชั่นการเรียกซ้ำแบบไม่หางเนื่องจากการเรียกซ้ำแบบหางสามารถปรับให้เหมาะสมโดยคอมไพเลอร์


1

ในขณะที่พวกเขาทั้งคู่อยู่ในระดับความซับซ้อนเดียวกันนั่นไม่ได้หมายความว่าพวกเขาทั้งสองมีรันไทม์เดียวกัน Quicksort มักจะเร็วกว่าการผสานเพียงเพราะมันง่ายต่อการเขียนโค้ดการใช้งานที่แน่นหนาและการดำเนินการที่ทำได้เร็วกว่า เป็นเพราะ quicksort นั้นโดยทั่วไปเร็วกว่าที่คนใช้แทนการผสาน

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


1

ทุกสิ่งเท่าเทียมกันฉันคาดหวังว่าคนส่วนใหญ่จะใช้สิ่งที่มีอยู่ให้สะดวกที่สุดและนั่นก็คือ qsort (3) นอกเหนือจาก quicksort นั้นเป็นที่รู้กันว่ารวดเร็วมากในอาร์เรย์เช่นเดียวกับการผสานคือตัวเลือกทั่วไปสำหรับรายการ

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

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

ในข่าวอื่น ๆ ลอยสามารถจัดเรียงเป็นจำนวนเต็ม แต่คุณต้องเปลี่ยนจำนวนลบรอบหลังจากนั้น

แก้ไข: อันที่จริงนี่เป็นทางที่ชั่วร้ายมากขึ้นในการจัดเรียงลอย as-จำนวนเต็ม: http://www.stereopsis.com/radix.html โปรดทราบว่าเคล็ดลับการพลิกบิตสามารถใช้โดยไม่คำนึงถึงอัลกอริทึมการเรียงลำดับที่คุณใช้จริง ...


1
ฉันเคยเห็นส่วนแบ่ง radix ของฉัน แต่มันค่อนข้างยากที่จะใช้เพราะถ้าวิเคราะห์อย่างถูกต้องรันไทม์ของมันไม่ใช่ O (n) เนื่องจากมันขึ้นอยู่กับจำนวนขององค์ประกอบอินพุต โดยทั่วไปแล้วมันยากมากที่จะทำการคาดการณ์ที่แข็งแกร่งซึ่งการเรียงลำดับของ Radix จะต้องมีประสิทธิภาพเกี่ยวกับอินพุต
Konrad Rudolph

มันคือ O (n) โดยที่ n คือรวมขนาดการป้อนข้อมูลที่เป็นรวมทั้งขนาดขององค์ประกอบ มันเป็นความจริงที่คุณสามารถนำไปใช้งานได้ดังนั้นคุณจะต้องมีจำนวนศูนย์เป็นจำนวนมาก แต่มันไร้สาระที่จะใช้การปรับใช้ที่ไม่ดีเพื่อการเปรียบเทียบ (ที่กล่าวว่าการดำเนินการอาจเป็นเรื่องยาก ymmv.)
Anders Eurenius

โปรดทราบว่าถ้าคุณใช้ GNU libc qsortการรวมแบบผสาน
เจสัน Orendorff

เอ่อจะแม่นยำมันเป็นประเภทผสานเว้นแต่หน่วยความจำชั่วคราวที่จำเป็นไม่สามารถจัดสรรได้ cvs.savannah.gnu.org/viewvc/libc/stdlib/...
เจสัน Orendorff

1

ส่วนเพิ่มเติมเล็ก ๆ น้อย ๆ เทียบกับการผสานอย่างรวดเร็ว

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

ตัวอย่างเช่นเราจัดเรียงรายการโดยใช้โปรโตคอลเครือข่ายบนเซิร์ฟเวอร์ระยะไกล

นอกจากนี้ในคอนเทนเนอร์แบบกำหนดเองเช่น "รายการที่ลิงก์" ประโยชน์ของการจัดเรียงแบบรวดเร็ว
1. รวมการเรียงลำดับในรายการที่เชื่อมโยงไม่ต้องการหน่วยความจำเพิ่มเติม 2. การเข้าถึงองค์ประกอบในการเรียงลำดับด่วนไม่ได้เรียงตามลำดับ (ในหน่วยความจำ)


0

Quick sort เป็นอัลกอริธึมการเรียงลำดับแบบ in-place ดังนั้นจึงเหมาะสำหรับอาร์เรย์ การเรียงแบบผสานในทางกลับกันต้องใช้พื้นที่จัดเก็บพิเศษของ O (N) และเหมาะสำหรับรายการที่เชื่อมโยง

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

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

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

แก้ไข: ฉันเพิ่งพบว่าการเรียงลำดับการจัดเรียงที่แย่ที่สุด / ดีที่สุด / avg เป็น nlogn เสมอ แต่การเรียงลำดับอย่างรวดเร็วอาจแตกต่างจาก n2 (กรณีที่แย่ที่สุดเมื่อองค์ประกอบเรียงลำดับแล้ว) เป็น nlogn (กรณีเฉลี่ย / ที่ดีที่สุดเมื่อ pivot หารอาร์เรย์ในสองเสมอ แบ่งเท่า ๆ กัน)


0

พิจารณาความซับซ้อนของเวลาและสถานที่ทั้งสอง สำหรับการเรียงแบบผสาน: ความซับซ้อนของเวลา: O (nlogn), ความซับซ้อนของพื้นที่: O (nlogn)

สำหรับการเรียงลำดับด่วน: ความซับซ้อนของเวลา: O (n ^ 2), ความซับซ้อนของพื้นที่: O (n)

ตอนนี้พวกเขาทั้งสองชนะในหนึ่งฉากแต่ละฉาก แต่การใช้เดือยแบบสุ่มคุณสามารถลดความซับซ้อนของเวลาในการเรียงลำดับแบบด่วนเป็น O (nlogn) ได้เกือบทุกครั้ง

ดังนั้นการเรียงแบบด่วนจึงเป็นที่ต้องการในหลาย ๆ แอปพลิเคชั่นแทนที่จะเป็น Merge sort


-1

ใน c / c ++ ที่ดินเมื่อไม่ได้ใช้คอนเทนเนอร์ stl ฉันมักจะใช้ quicksort เพราะมันถูกสร้างขึ้นในเวลาทำงานในขณะที่การรวมกันไม่ได้

ดังนั้นฉันเชื่อว่าในหลาย ๆ กรณีมันเป็นเพียงเส้นทางของการต่อต้านน้อยที่สุด

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


3
อันที่จริงถ้ามันเป็นฟังก์ชั่นห้องสมุด qsort () ที่คุณกำลังพูดถึงมันอาจจะหรือไม่อาจนำมาใช้เป็น Quicksort
Thomas Padron-McCarthy

3
คอนราดต้องขออภัยที่เกี่ยวกับเรื่องนี้เล็กน้อย แต่คุณพบว่าการรับประกันนั้นอยู่ที่ไหน ฉันไม่พบมันในมาตรฐาน ISO C หรือในมาตรฐาน C ++
โทมัส Padron-McCarthy

2
GNU libc's qsortเป็นการผสานการจัดเรียงเว้นแต่ว่าองค์ประกอบจะใหญ่โตอย่างแท้จริงหรือไม่สามารถจัดสรรหน่วยความจำชั่วคราวได้ cvs.savannah.gnu.org/viewvc/libc/stdlib/ …
Jason Orendorff

-3

หนึ่งในเหตุผลคือปรัชญามากขึ้น Quicksort คือ Top-> Down ปรัชญา ด้วยองค์ประกอบ n เรียงลำดับมี n! ความเป็นไปได้ ด้วยสองส่วนของ m & nm ซึ่งไม่เหมือนกันซึ่งกันและกันจำนวนของความเป็นไปได้ลดลงตามลำดับความสำคัญหลายประการ m! * (nm)! มีขนาดเล็กโดยคำสั่งหลายกว่า n! คนเดียว ลองนึกภาพ 5! กับ 3! * 2 !. 5! มีความเป็นไปได้ 10 เท่ามากกว่า 2 พาร์ติชั่น 2 และ 3 ในแต่ละส่วน และประมาณ 1 ล้านแฟคทอเรียลเทียบกับ 900K! * 100K! vs. ดังนั้นแทนที่จะกังวลเกี่ยวกับการสร้างใบสั่งใด ๆ ภายในช่วงหรือพาร์ติชันเพียงแค่สร้างใบสั่งในระดับที่กว้างขึ้นในพาร์ติชันและลดความเป็นไปได้ภายในพาร์ติชัน คำสั่งซื้อใด ๆ ที่จัดตั้งขึ้นก่อนหน้านี้ภายในขอบเขตจะถูกรบกวนในภายหลังหากพาร์ติชันไม่ได้เกิดร่วมกัน

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

การจัดเรียงด่วนเป็นเหมือนวิธีการจัดการที่ไม่มีใครสนใจในตอนแรกเกี่ยวกับคำสั่งใด ๆ เพียงเกี่ยวกับการประชุมเกณฑ์กว้าง ๆ โดยไม่คำนึงถึงคำสั่งซื้อ จากนั้นพาร์ติชันจะถูกลดขนาดลงจนกว่าคุณจะได้ชุดเรียงลำดับ ความท้าทายที่แท้จริงใน Quicksort คือการหาพาร์ติชันหรือเกณฑ์ในที่มืดเมื่อคุณไม่รู้อะไรเกี่ยวกับองค์ประกอบที่จะเรียงลำดับ นั่นคือเหตุผลที่เราต้องใช้ความพยายามเพื่อหาค่ามัธยฐานหรือเลือก 1 แบบสุ่มหรือวิธีการ "จัดการ" ตามอำเภอใจ เพื่อหาค่ามัธยฐานที่สมบูรณ์สามารถใช้ความพยายามอย่างมากและนำไปสู่วิธีการจากล่างขึ้นบนงี่เง่าอีกครั้ง ดังนั้น Quicksort กล่าวเพียงแค่เลือกเดือยสุ่มและหวังว่ามันจะอยู่ตรงกลางหรือทำงานเพื่อหาค่ามัธยฐานของ 3, 5 หรือมากกว่าเพื่อหาค่ามัธยฐานที่ดีกว่า แต่ไม่ได้วางแผนที่จะสมบูรณ์แบบ & ไม่ ' ไม่ต้องเสียเวลาในการสั่งซื้อครั้งแรก ดูเหมือนว่าจะทำได้ดีถ้าคุณโชคดีหรือบางครั้งลดน้อยลงเป็น n ^ 2 เมื่อคุณไม่ได้รับค่ามัธยฐาน แต่ใช้โอกาส ข้อมูลใด ๆ ที่เป็นแบบสุ่ม ขวา. ดังนั้นฉันจึงเห็นด้วยกับวิธีการทางด้านบนของตรรกะแบบเร็ว -> และปรากฎว่ามีโอกาสที่จะเลือก pivot & การเปรียบเทียบที่บันทึกไว้ก่อนหน้านี้ดูเหมือนว่าจะทำงานได้ดีกว่าเวลาด้านล่างที่พิถีพิถันและมีเสถียรภาพ -> เรียงลำดับการผสาน แต่ การเปรียบเทียบว่าจะช่วยประหยัดก่อนหน้านี้ดูเหมือนว่าจะทำงานได้ดีขึ้นกว่าช่วงล่างที่มีความพิถีพิถันและมีเสถียรภาพ -> วิธีการขึ้นเช่นการผสาน แต่ การเปรียบเทียบว่าจะช่วยประหยัดก่อนหน้านี้ดูเหมือนว่าจะทำงานได้ดีขึ้นกว่าช่วงล่างที่มีความพิถีพิถันและมีเสถียรภาพ -> วิธีการขึ้นเช่นการผสาน แต่


ผลประโยชน์ด่วนจากการสุ่มเลือก pivot เดือยสุ่มจะมีแนวโน้มไปทางพาร์ติชั่น 50:50 โดยธรรมชาติและไม่น่าจะมีความต่อเนื่องของหนึ่งในสุดขั้ว ปัจจัยคงที่ของ nlogn นั้นค่อนข้างต่ำจนถึงการแบ่งพาร์ติชันโดยเฉลี่ยคือ 60-40 หรือแม้กระทั่งจนถึง 70-30
Winter Melon

นี่เป็นเรื่องไร้สาระที่สมบูรณ์ quicksort ถูกใช้เนื่องจากประสิทธิภาพไม่ใช่ "ปรัชญา" ... และการอ้างถึง "คำสั่งถูกผูกมัดว่าจะหายไป" นั้นเป็นเพียงแค่เท็จ
Jim Balter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.