Tìm median trong O(n log n)
- Cách đơn giản nhất là sắp xếp danh sách rồi chọn giá trị ở giữa
- Độ phức tạp thời gian của sắp xếp dựa trên so sánh là
O(n log n) - Ví dụ mã:
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])
Tìm median trong O(n) trung bình
-
Có thể dùng thuật toán "quickselect" để tìm median với thời gian tuyến tính trung bình
-
Thuật toán đệ quy do Tony Hoare phát triển
-
Quy trình:
- Chọn một chỉ số ngẫu nhiên trong danh sách và đặt làm pivot
- Chia danh sách thành hai nhóm dựa trên pivot:
- các phần tử nhỏ hơn hoặc bằng pivot
- các phần tử lớn hơn pivot
- Đệ quy tìm tiếp trong nhóm có chứa median
-
Ví dụ mã:
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)
Chứng minh O(n) trung bình
- Nếu pivot chia danh sách gần như làm đôi, mỗi lần gọi đệ quy sẽ chỉ xử lý một nửa dữ liệu của bước trước
- Chứng minh toán học:
C = n + n/2 + n/4 + n/8 + ... = 2n = O(n)
O(n) xác định
-
Bảo đảm thời gian tuyến tính ngay cả trong trường hợp xấu nhất
-
Dùng thuật toán "median-of-medians" để chọn pivot
-
Quy trình:
- Chia danh sách thành các nhóm 5 phần tử
- Sắp xếp từng nhóm và chọn median
- Chọn median của các median làm pivot
-
Ví dụ mã:
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)]
Tóm tắt
- Thuật toán quickselect có thể tìm median trong thời gian tuyến tính nếu chọn được pivot phù hợp
- Thuật toán median-of-medians là cách chọn pivot bảo đảm thời gian tuyến tính ngay cả trong trường hợp xấu nhất
- Trong thực tế, chọn pivot ngẫu nhiên thường đã đủ hiệu quả
Tổng kết của GN⁺
- Bài viết này giải thích nhiều thuật toán khác nhau để tìm median, đặc biệt tập trung vào cách tìm median trong thời gian tuyến tính
- Thuật toán quickselect nhanh về trung bình, nhưng có thể dùng median-of-medians để phòng trường hợp xấu nhất
- Trong các ứng dụng thực tế, chọn pivot ngẫu nhiên thường là đủ hiệu quả trong phần lớn trường hợp
- Một thuật toán có chức năng tương tự là introselect, hiện được dùng trong thư viện chuẩn C++
1 bình luận
Ý kiến trên Hacker News