การแบ่งพาร์ติชันอย่างรวดเร็ว: Hoare vs. Lomuto


82

มีวิธีการแบ่งพาร์ติชันแบบ quicksort สองวิธีที่กล่าวถึงใน Cormen:

Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
    repeat
        j = j - 1
    until A[j] <= x
    repeat
        i = i + 1
    until A[i] >= x
    if i < j
        swap( A[i], A[j] )
    else
        return j

และ:

Lomuto-Partition(A, p, r)
x = A[r]
i = p - 1
for j = p to r - 1
    if A[j] <= x
        i = i + 1
        swap( A[i], A[j] )
swap( A[i +1], A[r] )
return i + 1

ไม่คำนึงถึงวิธีการเลือกเดือยในสถานการณ์ใดที่เป็นที่นิยมกัน ฉันรู้เช่นว่า Lomuto ขึ้นรูปได้ไม่ดีเมื่อมีค่าซ้ำซ้อนสูง (เช่นเมื่อพูดมากกว่า 2 / 3rds อาร์เรย์เป็นค่าเดียวกัน) ซึ่ง Hoare ทำงานได้ดีในสถานการณ์นั้น

มีกรณีพิเศษอื่นใดที่ทำให้วิธีการหนึ่งในการแบ่งพาร์ติชั่นดีกว่าอีกอย่าง?


2
ฉันไม่สามารถนึกถึงสถานการณ์ใด ๆที่ Lomuto นั้นดีกว่า Hoare ดูเหมือนว่า Lomuto A[i+1] <= xดำเนินการแลกเปลี่ยนพิเศษเมื่อใดก็ตาม ในอาร์เรย์ที่เรียงลำดับ (และได้รับ pivots ที่เลือกอย่างสมเหตุสมผล) Hoare ทำเกือบจะไม่มีการแลกเปลี่ยนและ Lomuto ทำตัน (เมื่อ j มีขนาดเล็กพอแล้วก็มีทั้งหมดA[j] <= x) ฉันหายไปอะไร?
Wandering Logic

2
@WanderingLogic ฉันไม่แน่ใจ แต่ดูเหมือนว่าการตัดสินใจของ Cormen ในการใช้พาร์ติชัน Lomuto ในหนังสือของเขาอาจจะสอนได้ - ดูเหมือนว่าจะมีค่าคงที่ของวงวนที่ค่อนข้างตรงไปตรงมา
Robert S. Barnes

2
โปรดทราบว่าอัลกอริทึมทั้งสองนั้นไม่ได้ทำสิ่งเดียวกัน ในตอนท้ายของอัลกอริธึมของ Hoare เดือยไม่ได้อยู่ที่จุดสุดท้าย คุณสามารถเพิ่ม a swap(A[p], A[j])ต่อท้าย Hoare's เพื่อให้มีพฤติกรรมเหมือนกันทั้งคู่
Aurélien Ooms

คุณควรตรวจสอบi < jในการวนซ้ำ 2 ครั้งของการแบ่งพาร์ติชันของ Hoare
Aurélien Ooms

@ AurélienOomsรหัสถูกคัดลอกโดยตรงจากหนังสือ
Robert S. Barnes

คำตอบ:


92

มิติการสอน

เนื่องจากความเรียบง่ายของวิธีการแบ่งพาร์ติชันของ Lomuto อาจทำให้การนำไปใช้ง่ายขึ้น มีเรื่องเล็ก ๆ น้อย ๆ ที่น่าสนใจในการเขียนโปรแกรม Pearlของ Jon Bentley ในการจัดเรียง:

“ การสนทนาส่วนใหญ่ของ Quicksort ใช้รูปแบบการแบ่งพาร์ติชันตามดัชนีที่กำลังจะมาถึง [... ] [ie Hoare's] แม้ว่าแนวคิดพื้นฐานของรูปแบบนั้นจะตรงไปตรงมา แต่ฉันพบรายละเอียดที่ยุ่งยากเสมอ - ฉันเคยใช้เวลาส่วนที่ดีกว่าในสองวันในการไล่ล่าบั๊กที่ซ่อนอยู่ในลูปการแบ่งพาร์ติชันสั้น ๆ ผู้อ่านร่างเบื้องต้นบ่นว่าวิธีมาตรฐานสองดัชนีนั้นง่ายกว่าของ Lomuto และร่างรหัสบางอย่างเพื่อให้ประเด็นของเขา; ฉันหยุดดูแลฉันพบข้อบกพร่องสองข้อ”

มิติประสิทธิภาพ

สำหรับการใช้งานจริงความสะดวกในการใช้งานอาจต้องเสียสละเพื่อประโยชน์ของประสิทธิภาพ บนพื้นฐานทางทฤษฎีเราสามารถกำหนดจำนวนของการเปรียบเทียบองค์ประกอบและการแลกเปลี่ยนเพื่อเปรียบเทียบประสิทธิภาพ นอกจากนี้เวลาทำงานจริงจะได้รับอิทธิพลจากปัจจัยอื่น ๆ เช่นประสิทธิภาพการแคชและการคาดคะเนความผิดพลาดของสาขา

ที่แสดงด้านล่างขั้นตอนวิธีปฏิบัติตนที่คล้ายกันมากในพีชคณิตแบบสุ่มยกเว้นสำหรับจำนวนของสัญญาแลกเปลี่ยน Lomuto ต้องการสามเท่าโฮฮะ

จำนวนการเปรียบเทียบ

n1n

จำนวนของการแลกเปลี่ยน

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

1,,n

วิธีการของ Lomuto

jA[j]x1,,nx1xx1x

{1,,n}1n

1nx=1n(x1)=n212.

n

วิธีการของ Hoare

x

ijxij

x

Hyp(n1,nx,x1)nxx1(nx)(x1)/(n1)x

สุดท้ายเราก็เฉลี่ยค่า pivot ทั้งหมดอีกครั้งเพื่อรับจำนวน swaps โดยรวมที่คาดไว้สำหรับการแบ่งพาร์ติชันของ Hoare:

1nx=1n(nx)(x1)n1=n613.

(รายละเอียดเพิ่มเติมสามารถพบได้ในวิทยานิพนธ์ของฉันหน้า 29)

รูปแบบการเข้าถึงหน่วยความจำ

ขั้นตอนวิธีการทั้งสองใช้สองชี้ลงในอาร์เรย์ที่สแกนตามลำดับ ดังนั้นทั้งสองจึงทำงานแคช wrt ที่ดีที่สุด

องค์ประกอบที่เท่าเทียมกันและรายการที่เรียงลำดับแล้ว

ตามที่กล่าวไว้แล้วโดย Wandering Logic ประสิทธิภาพของอัลกอริทึมนั้นแตกต่างกันอย่างมากสำหรับรายการที่ไม่ใช่การเรียงสับเปลี่ยนแบบสุ่ม

n/2

0ijO(nlogn)

0A[j] <= xi=nΘ(n2)

ข้อสรุป

วิธีการของ Lomuto นั้นง่ายและง่ายต่อการนำไปใช้ แต่ไม่ควรใช้สำหรับการนำวิธีการเรียงลำดับของไลบรารีมาใช้


16
ว้าวนั่นเป็นคำตอบโดยละเอียด ทำได้ดีมาก!
กราฟิลส์

ต้องเห็นด้วยกับราฟาเอลคำตอบที่ดีจริงๆ!
Robert S. Barnes

1
ฉันจะอธิบายให้กระจ่างขึ้นเล็กน้อยว่าอัตราส่วนขององค์ประกอบเฉพาะต่อองค์ประกอบทั้งหมดลดลงจำนวนการเปรียบเทียบที่ Lomuto เติบโตเร็วกว่าของ Hoare อย่างมาก นี่อาจเป็นเพราะการแบ่งส่วนที่ไม่ดีในส่วนของ Lomuto และการแบ่งส่วนที่ดีโดยเฉลี่ยในส่วนของ Hoare
Robert S. Barnes

คำอธิบายที่ยอดเยี่ยมของทั้งสองวิธี! ขอขอบคุณ!
v kouk

คุณสามารถสร้างตัวแปรของวิธี Lomuto ที่สามารถแยกองค์ประกอบทั้งหมดที่เท่ากับ pivot และปล่อยให้พวกเขาออกจากการเรียกซ้ำแม้ว่าฉันจะไม่แน่ใจว่ามันจะช่วยหรือขัดขวางกรณีเฉลี่ย
Jakub Narębski

5

ความคิดเห็นบางส่วนถูกเพิ่มเข้าไปในคำตอบของเซบาสเตียนที่ยอดเยี่ยม

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

ความมั่นคง

อัลกอริทึม Lomuto เป็นsemistable : ลำดับญาติขององค์ประกอบที่ไม่พอใจคำกริยาจะถูกรักษาไว้ อัลกอริทึมของ Hoare ไม่เสถียร

รูปแบบการเข้าถึงองค์ประกอบ

อัลกอริทึมของ Lomuto สามารถใช้กับลิสต์ที่เชื่อมโยงเดี่ยว ๆ หรือโครงสร้างข้อมูลแบบส่งต่ออย่างเดียวที่คล้ายกัน อัลกอริทึมของโฮร์ต้องการbidirectionality

จำนวนการเปรียบเทียบ

n1n

แต่เพื่อที่จะทำสิ่งนี้เราต้องเสียสละ 2 คุณสมบัติ:

  1. ลำดับที่จะแบ่งพาร์ติชันต้องไม่ว่างเปล่า
  2. อัลกอริทึมไม่สามารถส่งคืนจุดพาร์ติชัน

n

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