การหาค่ามัธยฐานแบบ O(n log n)
- วิธีที่ง่ายที่สุดคือจัดเรียงลิสต์แล้วเลือกค่ามัธยฐาน
- ความซับซ้อนเชิงเวลาของการจัดเรียงแบบอิงการเปรียบเทียบคือ
O(n log n) - ตัวอย่างโค้ด:
def nlogn_median(l): l = sorted(l) if len(l) % 2 == 1: return l[len(l) // 2] else: return 0.5 * (l[len(l) // 2 - 1] + l[len(l) // 2])
การหาค่ามัธยฐานแบบ O(n) โดยเฉลี่ย
-
สามารถใช้อัลกอริทึม "quickselect" เพื่อหาค่ามัธยฐานได้ในเวลาเชิงเส้นโดยเฉลี่ย
-
เป็นอัลกอริทึมแบบเรียกซ้ำที่ Tony Hoare พัฒนาขึ้น
-
ขั้นตอน:
- เลือกดัชนีแบบสุ่มจากลิสต์แล้วตั้งเป็น pivot
- แบ่งลิสต์ออกเป็นสองกลุ่มโดยอิงจาก pivot:
- องค์ประกอบที่น้อยกว่าหรือเท่ากับ pivot
- องค์ประกอบที่มากกว่า pivot
- ค้นหาแบบเรียกซ้ำในกลุ่มที่มีค่ามัธยฐานอยู่
-
ตัวอย่างโค้ด:
import random def quickselect_median(l, pivot_fn=random.choice): if len(l) % 2 == 1: return quickselect(l, len(l) // 2, pivot_fn) else: return 0.5 * (quickselect(l, len(l) // 2 - 1, pivot_fn) + quickselect(l, len(l) // 2, pivot_fn)) def quickselect(l, k, pivot_fn): if len(l) == 1: assert k == 0 return l[0] pivot = pivot_fn(l) lows = [el for el in l if el < pivot] highs = [el for el in l if el > pivot] pivots = [el for el in l if el == pivot] if k < len(lows): return quickselect(lows, k, pivot_fn) elif k < len(lows) + len(pivots): return pivots[0] else: return quickselect(highs, k - len(lows) - len(pivots), pivot_fn)
การพิสูจน์ค่าเฉลี่ย O(n)
- หาก pivot แบ่งลิสต์ออกได้ประมาณครึ่งหนึ่ง การเรียกซ้ำแต่ละครั้งจะทำงานกับข้อมูลเพียงครึ่งหนึ่งของขั้นก่อนหน้า
- การพิสูจน์ทางคณิตศาสตร์:
C = n + n/2 + n/4 + n/8 + ... = 2n = O(n)
O(n) แบบกำหนดแน่นอน
-
รับประกันเวลาเชิงเส้นแม้ในกรณีเลวร้ายที่สุด
-
ใช้อัลกอริทึม "median-of-medians" เพื่อเลือก pivot
-
ขั้นตอน:
- แบ่งลิสต์ออกเป็นกลุ่มละ 5 ค่า
- จัดเรียงแต่ละกลุ่มแล้วเลือกค่ามัธยฐาน
- เลือกค่ามัธยฐานของค่ามัธยฐานทั้งหมดมาเป็น pivot
-
ตัวอย่างโค้ด:
def pick_pivot(l): assert len(l) > 0 if len(l) < 5: return nlogn_median(l) chunks = chunked(l, 5) full_chunks = [chunk for chunk in chunks if len(chunk) == 5] sorted_groups = [sorted(chunk) for chunk in full_chunks] medians = [chunk[2] for chunk in sorted_groups] median_of_medians = quickselect_median(medians, pick_pivot) return median_of_medians def chunked(l, chunk_size): return [l[i:i + chunk_size] for i in range(0, len(l), chunk_size)]
สรุป
- อัลกอริทึม quickselect สามารถหาค่ามัธยฐานได้ในเวลาเชิงเส้น หากเลือก pivot ได้เหมาะสม
- อัลกอริทึม median-of-medians เป็นวิธีเลือก pivot ที่รับประกันเวลาเชิงเส้นแม้ในกรณีเลวร้ายที่สุด
- ในทางปฏิบัติ การเลือก pivot แบบสุ่มมักมีประสิทธิภาพดีเพียงพอ
สรุปโดย GN⁺
- บทความนี้อธิบายอัลกอริทึมหลายแบบสำหรับการหาค่ามัธยฐาน โดยเน้นเป็นพิเศษที่วิธีหาค่ามัธยฐานในเวลาเชิงเส้น
- อัลกอริทึม quickselect เร็วโดยเฉลี่ย แต่หากต้องการรับมือกับกรณีเลวร้ายที่สุด ก็สามารถใช้อัลกอริทึม median-of-medians ได้
- ในการใช้งานจริง การเลือก pivot แบบสุ่มมักมีประสิทธิภาพเพียงพอในเกือบทุกกรณี
- อัลกอริทึมที่มีฟังก์ชันคล้ายกันอีกตัวคือ introselect ซึ่งถูกใช้ในไลบรารีมาตรฐานของ C++
1 ความคิดเห็น
ความคิดเห็นบน Hacker News