AlgoVerse

انتخاب سریع (k امین کوچک‌ترین)

مثل مرتب‌سازی سریع افراز می‌کند، ولی فقط در سمتی که k امین عضو در آن است ادامه می‌دهد؛ به‌طور متوسط زمان خطی.

برای پیدا کردن k امین کوچک‌ترین عضو (مثلاً میانه) لازم نیست کل آرایه را مرتب کنیم. یک افراز مثل مرتب‌سازی سریع انجام بده: محور در جای نهایی‌اش می‌نشیند، کوچک‌ترها چپش و بزرگ‌ترها راستش. اگر رتبه‌ی محور همان k بود، تمام است؛ وگرنه فقط یک طرف را ادامه بده.

ادامه‌ی توضیح ↓
ورودی:
n=8 · k=4خروجی PDF

چهارمین کوچک‌ترینِ هشت عدد؛ فقط سمت لازم افراز می‌شود.

  1. def quickselect(a, k):
  2. lo = 0
  3. hi = len(a) - 1
  4. while True:
  5. if lo == hi:
  6. return a[lo]
  7. p = partition(a, lo, hi)
  8. rank = p - lo + 1
  9. if k == rank:
  10. return a[p]
  11. if k < rank:
  12. hi = p - 1
  13. else:
  14. k = k - rank
  15. lo = p + 1
  16. def partition(a, lo, hi):
  17. pivot = a[hi]
  18. i = lo - 1
  19. for j in range(lo, hi):
  20. if a[j] <= pivot:
  21. i = i + 1
  22. a[i], a[j] = a[j], a[i]
  23. a[i + 1], a[hi] = a[hi], a[i + 1]
  24. return i + 1

ناوردا: جواب همیشه k امین کوچک‌ترین عضو بازه‌ی a[lo..hi] است.

شروع اجرا

3081225314457667k = 4

متغیرهای محلی

k4

نارنجی = در همین گام عوض شد

a list

38251476

Call stack

  1. quickselect()خط 1
گام ۱ از ۶۶
خط سیاه = split · خط نارنجی = گام کلیدیcompare: 0/11 · swap: 0/8 · split: 0/2 · check: 0/2 · go_left: 0/1 · found: 0/1

    رشد تعداد عملیات با اندازه‌ی ورودی

    میانگین ۱۲ گراف تصادفی برای هر n (با seed ثابت). نشانگر توخالی = ورودی فعلی.

    • comparisons
    • ورودی فعلی (n=8)
    01020304046810121416n = تعداد عضوهای آرایهcomparisons
    نمایش به شکل جدول
    ncomparisons
    44.4
    67.4
    813.5
    1020
    1222.1
    1430.6
    1634.6
    توضیح

    ایده

    برای پیدا کردن k امین کوچک‌ترین عضو (مثلاً میانه) لازم نیست کل آرایه را مرتب کنیم. یک افراز مثل مرتب‌سازی سریع انجام بده: محور در جای نهایی‌اش می‌نشیند، کوچک‌ترها چپش و بزرگ‌ترها راستش. اگر رتبه‌ی محور همان k بود، تمام است؛ وگرنه فقط یک طرف را ادامه بده.

    هزینه

    • به‌طور متوسط هر افراز بازه را حدود نصف می‌کند: n + n/2 + n/4 + … ≈ 2n، یعنی O(n)، از مرتب کردن (n log n) بهتر.
    • در بدترین حالت (ورودی «مرتب، کوچک‌ترین»)، هر افراز فقط یک عضو کنار می‌گذارد: O(n²). CLRS با «میانه‌ی میانه‌ها» محوری تضمینی می‌سازد که بدترین حالت را هم خطی می‌کند.

    در trace به چه چیزی نگاه کنی

    • نوار آبی بازه‌ی lo..hi است که جواب در آن است؛ ببین بعد از هر افراز چطور کوچک می‌شود.
    • k بالای آرایه نوشته شده و با رفتن به سمت راست کم می‌شود.