2-bo‘lim

Samaradorlik va Big-O notatsiyasi

Algoritm tezligini o'lchash, murakkablik sinflari, Big-O notatsiyasini o'qish va xotira sarfini baholash.

🕑 11 daqiqa o‘qish 📄 917 so‘z 👁 6 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Nima uchun sekundomer yetarli emas?
  2. Big-O notatsiyasi
  3. Asosiy murakkablik sinflari
  4. Big-O ni hisoblash qoidalari
  5. 1-qoida: doimiylarni tashlab yuboring
  6. 2-qoida: eng tez o'sadigan hadni qoldiring
  7. 3-qoida: ichma-ich sikllar ko'paytiriladi
  8. 4-qoida: ketma-ket sikllar qo'shiladi
  9. Amaliy tahlil
  10. Xotira murakkabligi (Space Complexity)
  11. Eng yaxshi, o'rtacha va eng yomon holat
  12. Ma'lumotlar tuzilmalari taqqoslanishi
  13. Python amallarining murakkabligi
  14. Xulosa

O'tgan bo'limda bir masalaning ikki yechimi butunlay boshqa tezlikda ishlashini ko'rdik. Endi bu farqni o'lchashni o'rganamiz.

Nima uchun sekundomer yetarli emas? #

Tabiiy fikr: kodni ishga tushirib, vaqtini o'lchash. Lekin bu ishonchsiz:

MuammoIzoh
Kompyuterga bog'liqKuchli noutbukda tez, eski telefonda sekin
Tilga bog'liqC dagi kod Python dagidan 50 barobar tez
Yukka bog'liqFon dasturlari natijani o'zgartiradi
Ma'lumot hajmiga bog'liq10 ta element uchun hammasi tez ishlaydi

Bizga kompyuterga bog'liq bo'lmagan o'lchov kerak. Yechim - qadamlar sonini kirish hajmiga nisbatan hisoblash.

Big-O notatsiyasi #

Big-O algoritmning eng yomon holatdagi o'sish tezligini ko'rsatadi. n - kirish hajmi.

Python
def birinchi_elementni_ol(royxat):
    return royxat[0]              # 1 ta amal, royxat qanchalik katta bo'lmasin

Bu O(1) - doimiy vaqt.

Python
def yigindini_hisobla(royxat):
    natija = 0
    for element in royxat:        # n marta takrorlanadi
        natija += element
    return natija

Bu O(n) - chiziqli vaqt.

Python
def barcha_juftliklar(royxat):
    juftliklar = []
    for birinchi in royxat:           # n marta
        for ikkinchi in royxat:       # har biri uchun yana n marta
            juftliklar.append((birinchi, ikkinchi))
    return juftliklar

Bu O(n²) - kvadratik vaqt.

Asosiy murakkablik sinflari #

amallar n O(1) - doimiy O(log n) - logarifmik O(n) - chiziqli O(n log n) O(n²) O(2ⁿ) Ajoyib zona O(1), O(log n) Xavfli zona O(n²) va yomonroq
n oshgani sari murakkablik sinflari orasidagi farq keskin ortadi
Big-ONomiMisoln = 1000 da amallar
O(1)DoimiyMassiv elementiga murojaat1
O(log n)LogarifmikIkkilik qidiruv~10
O(n)ChiziqliRo'yxatni bir marta aylanish1 000
O(n log n)Chiziqli-logarifmikMerge sort~10 000
O(n²)KvadratikIchma-ich ikki sikl1 000 000
O(n³)KubikUch sikl1 000 000 000
O(2ⁿ)EksponensialRekursiv Fibonachchi10³⁰¹
O(n!)FaktorialBarcha o'rin almashtirishlarhisoblab bo'lmaydi
Raqamlar bilan tasavvur qiling

Bir amal 1 nanosekund vaqt oladi deb faraz qilaylik. n = 50 bo'lganda:

MurakkablikVaqt
O(n)50 nanosekund
O(n²)2.5 mikrosekund
O(2ⁿ)13 kun
O(n!)Koinot yoshidan uzoqroq

Shuning uchun eksponensial algoritm kichik kirishda ham yaroqsiz bo'lishi mumkin.

Big-O ni hisoblash qoidalari #

1-qoida: doimiylarni tashlab yuboring #

Python
def misol(royxat):
    print(royxat[0])              # O(1)
    for element in royxat:        # O(n)
        print(element)
    for element in royxat:        # yana O(n)
        print(element * 2)

Jami: 1 + n + n = 2n + 1. Big-O da bu O(n).

Sabab: n juda katta bo'lganda 2n + 1 va n bir xil tezlikda o'sadi. Bizni o'sish shakli qiziqtiradi, aniq raqam emas.

2-qoida: eng tez o'sadigan hadni qoldiring #

n² + 5n + 100O(n²)

n = 1000 bo'lganda: 1 000 000 + 5 000 + 100. Birinchi had qolganlarini butunlay bosib ketadi.

3-qoida: ichma-ich sikllar ko'paytiriladi #

Python
for i in range(n):            # n
    for j in range(n):        # har biri uchun n
        print(i, j)           # jami n * n

O(n²)

4-qoida: ketma-ket sikllar qo'shiladi #

Python
for i in range(n):        # n
    print(i)
for j in range(m):        # m
    print(j)

O(n + m) - agar n va m turli kattaliklar bo'lsa, ikkalasini ham yozing.

Amaliy tahlil #

Python
def takrorlanuvchi_bormi_sekin(royxat):
    """Har bir juftlikni tekshiradi - O(n^2)."""
    for i in range(len(royxat)):
        for j in range(i + 1, len(royxat)):
            if royxat[i] == royxat[j]:
                return True
    return False


def takrorlanuvchi_bormi_tez(royxat):
    """To'plamdan foydalanadi - O(n)."""
    korilganlar = set()
    for element in royxat:
        if element in korilganlar:
            return True
        korilganlar.add(element)
    return False

Ikkalasi ham to'g'ri javob beradi. Farqni o'lchab ko'ramiz:

Python
import time
import random

sonlar = random.sample(range(1_000_000), 10_000)

for nomi, funksiya in [("Sekin O(n^2)", takrorlanuvchi_bormi_sekin),
                       ("Tez O(n)", takrorlanuvchi_bormi_tez)]:
    boshlanish = time.perf_counter()
    funksiya(sonlar)
    davomiylik = time.perf_counter() - boshlanish
    print(f"{nomi:<16}{davomiylik:>8.4f} soniya")
Natija
Sekin O(n^2)      2.8431 soniya
Tez O(n)          0.0009 soniya

3000 barobar farq - va bu atigi 10 000 ta element uchun.

Xotira murakkabligi (Space Complexity) #

Vaqt yagona resurs emas. Algoritm qancha qo'shimcha xotira talab qilishi ham muhim.

Python
def teskari_yangi_royxat(royxat):
    """O(n) qo'shimcha xotira - yangi ro'yxat yaratadi."""
    natija = []
    for element in reversed(royxat):
        natija.append(element)
    return natija


def teskari_joyida(royxat):
    """O(1) qo'shimcha xotira - asl ro'yxatni o'zgartiradi."""
    chap, ong = 0, len(royxat) - 1
    while chap < ong:
        royxat[chap], royxat[ong] = royxat[ong], royxat[chap]
        chap += 1
        ong -= 1
    return royxat
Vaqt va xotira o'rtasidagi kelishuv

Ko'pincha tezlikni xotira hisobiga sotib olasiz. takrorlanuvchi_bormi_tez funksiyasi O(n) qo'shimcha xotira (to'plam) sarflab, vaqtni O(n²) dan O(n) ga tushirdi. Bu almashuv (trade-off) deb ataladi va deyarli har bir muhandislik qarorida uchraydi.

Eng yaxshi, o'rtacha va eng yomon holat #

Python
def qidir(royxat, qidirilayotgan):
    for indeks, element in enumerate(royxat):
        if element == qidirilayotgan:
            return indeks
    return -1
HolatVaziyatMurakkablik
Eng yaxshi (Ω)Element birinchi o'rindaO(1)
O'rtacha (Θ)Element o'rtadaO(n/2) = O(n)
Eng yomon (O)Element oxirida yoki yo'qO(n)

Amalda deyarli har doim eng yomon holat haqida gapiriladi - chunki dastur eng og'ir sharoitda ham ishlashi kerak.

Ma'lumotlar tuzilmalari taqqoslanishi #

Bu jadval butun darslik davomida sizga kerak bo'ladi:

TuzilmaMurojaatQidiruvQo'shishO'chirish
Massiv (list)O(1)O(n)O(n)O(n)
Bog'langan ro'yxatO(n)O(n)O(1)O(1)
StekO(n)O(n)O(1)O(1)
NavbatO(n)O(n)O(1)O(1)
Xesh-jadval-O(1)*O(1)*O(1)*
Ikkilik qidiruv daraxtiO(log n)*O(log n)*O(log n)*O(log n)*

* - o'rtacha holat. Eng yomon holatda xesh-jadval O(n), muvozanatsiz daraxt ham O(n) bo'lishi mumkin.

Python amallarining murakkabligi #

Ko'p dasturchi buni bilmaydi va bilmasdan sekin kod yozadi:

AmalMurakkablikIzoh
royxat[i]O(1)Indeks bo'yicha
royxat.append(x)O(1)Oxiriga
royxat.insert(0, x)O(n)Boshiga - hamma element suriladi
royxat.pop()O(1)Oxiridan
royxat.pop(0)O(n)Boshidan
x in royxatO(n)Ketma-ket qidiradi
x in toplamO(1)Xesh orqali
x in lugatO(1)Xesh orqali
lugat[k]O(1)Kalit bo'yicha
sorted(royxat)O(n log n)Timsort
len(royxat)O(1)Uzunlik saqlanadi
Eng ko'p uchraydigan sekinlik sababi
Python
# SEKIN - O(n^2), chunki har bir tekshiruv O(n)
natija = []
for element in katta_royxat:
    if element not in natija:
        natija.append(element)

# TEZ - O(n)
korilganlar = set()
natija = []
for element in katta_royxat:
    if element not in korilganlar:
        korilganlar.add(element)
        natija.append(element)
Amaliy topshiriq

Quyidagi funksiyalarning Big-O murakkabligini aniqlang va sababini yozing:

Python
def a(n):
    return n * 2

def b(royxat):
    for x in royxat:
        for y in royxat:
            for z in royxat:
                print(x, y, z)

def c(n):
    natija = 0
    while n > 1:
        n = n // 2
        natija += 1
    return natija

def d(royxat_1, royxat_2):
    for x in royxat_1:
        if x in royxat_2:
            print(x)

So'ng d funksiyasini O(n) ga optimallashtiring.

Xulosa #

  • Big-O algoritmning kirish hajmi oshganda qanday o'sishini ko'rsatadi.
  • Doimiylar va kichik hadlar tashlab yuboriladi: 3n² + 5n + 2O(n²).
  • Ichma-ich sikllar ko'paytiriladi, ketma-ket sikllar qo'shiladi.
  • Odatda eng yomon holat hisobga olinadi.
  • Xotira murakkabligi ham baholanishi kerak; tezlik va xotira o'rtasida almashuv bor.
  • in amali ro'yxatda O(n), to'plam va lug'atda O(1) - buni bilish kodni tez qiladi.

Keyingi bo'limda eng asosiy ma'lumotlar tuzilmasi - massiv bilan tanishamiz.

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.