13-bo‘lim
Oddiy saralash algoritmlari
Pufakcha, tanlash va qo'shish usuli bilan saralash - ularning ishlash prinsipi, kodi va taqqoslanishi.
Ushbu bo‘lim mundarijasi
Saralash - kompyuter fanidagi eng ko'p o'rganilgan masala. Bu bo'limda uchta sodda O(n²) algoritmni ko'ramiz. Ular sekin, lekin algoritmik fikrlashni o'rgatadi va kichik massivlarda amalda ham ishlatiladi.
Nima uchun saralash muhim? #
| Sabab | Izoh |
|---|---|
| Ikkilik qidiruv | Faqat tartiblangan massivda ishlaydi |
| Takrorlarni topish | Qo'shnilarni solishtirish yetarli |
| Mediana, kvartillar | Tartiblangan ma'lumot kerak |
| Ma'lumotni ko'rsatish | Foydalanuvchi tartibni kutadi |
1. Pufakcha usuli (Bubble Sort) #
Eng sodda algoritm: qo'shni juftliklarni solishtirib, kerak bo'lsa almashtiramiz. Katta qiymatlar "pufakcha" kabi yuqoriga suzib chiqadi.
def pufakcha_sarala(massiv):
"""Pufakcha usuli. O(n^2) vaqt, O(1) xotira, barqaror."""
sonlar = list(massiv)
n = len(sonlar)
for otish in range(n - 1):
almashtirildi = False
# Oxirgi `otish` ta element allaqachon o'z joyida
for i in range(n - 1 - otish):
if sonlar[i] > sonlar[i + 1]:
sonlar[i], sonlar[i + 1] = sonlar[i + 1], sonlar[i]
almashtirildi = True
if not almashtirildi: # allaqachon tartiblangan
break
return sonlar
print(pufakcha_sarala([5, 2, 8, 1, 9, 3]))
[1, 2, 3, 5, 8, 9]
almashtirildi bayrog'i muhimUsiz algoritm allaqachon tartiblangan massivda ham n² qadam bajaradi.
U bilan esa eng yaxshi holat O(n) ga tushadi - bitta o'tishda hech narsa almashmasa, ish tugagan.
2. Tanlash usuli (Selection Sort) #
Har bir o'tishda eng kichik elementni topib, uni boshiga qo'yamiz.
def tanlash_bilan_sarala(massiv):
"""Tanlash usuli. O(n^2) vaqt, O(1) xotira, barqaror emas."""
sonlar = list(massiv)
n = len(sonlar)
for i in range(n - 1):
eng_kichik_indeks = i
for j in range(i + 1, n):
if sonlar[j] < sonlar[eng_kichik_indeks]:
eng_kichik_indeks = j
if eng_kichik_indeks != i:
sonlar[i], sonlar[eng_kichik_indeks] = sonlar[eng_kichik_indeks], sonlar[i]
return sonlar
print(tanlash_bilan_sarala([5, 2, 8, 1, 9, 3]))
[1, 2, 3, 5, 8, 9]
Jarayonni ko'rsatamiz:
def tanlash_qadam_bilan(massiv):
sonlar = list(massiv)
n = len(sonlar)
for i in range(n - 1):
eng_kichik = i
for j in range(i + 1, n):
if sonlar[j] < sonlar[eng_kichik]:
eng_kichik = j
sonlar[i], sonlar[eng_kichik] = sonlar[eng_kichik], sonlar[i]
tartiblangan = " ".join(f"[{q}]" for q in sonlar[:i + 1])
qolgan = " ".join(f" {q} " for q in sonlar[i + 1:])
print(f" {i + 1}-qadam: {tartiblangan} {qolgan}")
return sonlar
tanlash_qadam_bilan([5, 2, 8, 1, 9, 3])
1-qadam: [1] 5 8 2 9 3
2-qadam: [1] [2] 8 5 9 3
3-qadam: [1] [2] [3] 5 9 8
4-qadam: [1] [2] [3] [5] 9 8
5-qadam: [1] [2] [3] [5] [8] 9
U eng kam almashtirish qiladi - ko'pi bilan n - 1 marta. Agar almashtirish juda qimmat
bo'lsa (masalan, katta obyektlarni ko'chirish yoki flesh xotiraga yozish), bu muhim bo'lishi mumkin.
Solishtirishlar soni esa har doim n²/2 - hatto tartiblangan massivda ham.
3. Qo'shish usuli (Insertion Sort) #
Qo'lingizdagi kartalarni tartiblashni tasavvur qiling: har bir yangi kartani allaqachon tartiblangan qism ichiga to'g'ri joyga qo'yasiz.
def qoshish_bilan_sarala(massiv):
"""Qo'shish usuli. O(n^2), lekin deyarli tartiblangan massivda O(n)."""
sonlar = list(massiv)
for i in range(1, len(sonlar)):
joriy = sonlar[i]
j = i - 1
# Kattaroq elementlarni o'ngga suramiz
while j >= 0 and sonlar[j] > joriy:
sonlar[j + 1] = sonlar[j]
j -= 1
sonlar[j + 1] = joriy
return sonlar
print(qoshish_bilan_sarala([5, 2, 8, 1, 9, 3]))
[1, 2, 3, 5, 8, 9]
U ikkita muhim xossaga ega:
- Deyarli tartiblangan massivda deyarli O(n) ishlaydi.
- Onlayn algoritm - ma'lumot oqim bilan kelayotgan bo'lsa ham ishlay oladi.
Aynan shu sabablardan Python'ning sorted() funksiyasi (Timsort) kichik bo'laklarni
qo'shish usuli bilan saralaydi. Java va C++ ham xuddi shunday qiladi.
Uchalasini taqqoslash #
import random
import time
def olcha(funksiya, ma_lumot):
boshlanish = time.perf_counter()
funksiya(ma_lumot)
return time.perf_counter() - boshlanish
N = 2000
tasodifiy = random.sample(range(N * 10), N)
tartiblangan = sorted(tasodifiy)
teskari = tartiblangan[::-1]
algoritmlar = [
("Pufakcha", pufakcha_sarala),
("Tanlash", tanlash_bilan_sarala),
("Qo'shish", qoshish_bilan_sarala),
("sorted() [Timsort]", sorted),
]
print(f"{'Algoritm':<22}{'Tasodifiy':>12}{'Tartiblangan':>14}{'Teskari':>12}")
print("-" * 60)
for nomi, funksiya in algoritmlar:
a = olcha(funksiya, tasodifiy)
b = olcha(funksiya, tartiblangan)
c = olcha(funksiya, teskari)
print(f"{nomi:<22}{a:>11.4f}s{b:>13.4f}s{c:>11.4f}s")
Algoritm Tasodifiy Tartiblangan Teskari
------------------------------------------------------------
Pufakcha 0.4821s 0.0002s 0.6013s
Tanlash 0.1904s 0.1887s 0.1921s
Qo'shish 0.2312s 0.0004s 0.4605s
sorted() [Timsort] 0.0004s 0.0000s 0.0000s
Bu jadval juda ko'p narsani ko'rsatadi:
- Pufakcha va qo'shish tartiblangan ma'lumotda deyarli bepul ishlaydi.
- Tanlash har doim bir xil - u ma'lumot holatini umuman hisobga olmaydi.
- Timsort boshqa darajadagi algoritm - keyingi bo'limda uni ko'ramiz.
Barqarorlik (Stability) #
Barqaror algoritm teng elementlarning asl tartibini saqlaydi. Bu ko'p hollarda muhim.
talabalar = [
("Husanboy", 85),
("Sardor", 92),
("Malika", 85),
("Bekzod", 78),
]
# Avval ism bo'yicha, keyin baho bo'yicha saralaymiz
ism_boyicha = sorted(talabalar, key=lambda t: t[0])
baho_boyicha = sorted(ism_boyicha, key=lambda t: t[1], reverse=True)
for ism, baho in baho_boyicha:
print(f"{ism:<10}{baho}")
Sardor 92
Husanboy 85
Malika 85
Bekzod 78
85 ballli Husanboy va Malika alifbo tartibida qoldi - chunki sorted() barqaror.
| Algoritm | Barqarormi |
|---|---|
| Pufakcha | Ha |
| Qo'shish | Ha |
| Tanlash | Yo'q |
| Merge sort | Ha |
| Quick sort | Yo'q |
| Heap sort | Yo'q |
| Timsort (Python) | Ha |
Umumiy taqqoslash #
| Algoritm | Eng yaxshi | O'rtacha | Eng yomon | Xotira | Barqaror |
|---|---|---|---|---|---|
| Pufakcha | O(n) | O(n²) | O(n²) | O(1) | Ha |
| Tanlash | O(n²) | O(n²) | O(n²) | O(1) | Yo'q |
| Qo'shish | O(n) | O(n²) | O(n²) | O(1) | Ha |
Ular o'quv maqsadida muhim, lekin real kodda har doim sorted() yoki list.sort() ishlating.
Ular C tilida yozilgan, o'nlab yil sinovdan o'tgan va sizning kodingizdan yuzlab barobar tez.
Yagona istisno - massiv juda kichik (taxminan 10-20 elementgacha) bo'lsa, qo'shish usuli murakkab algoritmlardan tezroq bo'lishi mumkin. Aynan shuning uchun Timsort ichkarida undan foydalanadi.
- Pufakcha usulini ikki tomonlama qiling (shaker sort): bir o'tishda o'ngga, keyingisida chapga.
- Qo'shish usulida joyni ikkilik qidiruv bilan toping. Solishtirishlar soni kamayadimi? Ko'chirishlar-chi?
- Talabalar ro'yxatini avval guruh, keyin baho bo'yicha saralang - barqarorlikdan foydalaning.
- Har bir algoritm uchun solishtirishlar va almashtirishlar sonini sanovchi hisoblagich qo'shing.
Xulosa #
- Uchala algoritm ham O(n²) va faqat kichik massivlar uchun mos.
- Pufakcha: sodda,
almashtirildibayrog'i bilan tartiblangan massivda O(n). - Tanlash: eng kam almashtirish, lekin har doim O(n²).
- Qo'shish: deyarli tartiblangan ma'lumotda juda tez, onlayn ishlaydi, barqaror.
- Barqarorlik - teng elementlar tartibining saqlanishi; ko'p bosqichli saralashda muhim.
- Amalda har doim
sorted()ishlating.
Keyingi bo'limda haqiqiy, O(n log n) saralash algoritmlarini ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.