Lietuvių

Išsamus vadovas apie Didžiojo O notaciją, algoritmų sudėtingumo analizę ir našumo optimizavimą programinės įrangos inžinieriams visame pasaulyje.

Didžiojo O notacija: Algoritmų sudėtingumo analizė

Programinės įrangos kūrimo pasaulyje funkcionalaus kodo rašymas yra tik pusė darbo. Ne mažiau svarbu užtikrinti, kad jūsų kodas veiktų efektyviai, ypač kai jūsų programos plečiasi ir tvarko didesnius duomenų rinkinius. Čia ir įsijungia Didžiojo O notacija. Didžiojo O notacija yra esminis įrankis algoritmų veikimui suprasti ir analizuoti. Šis vadovas pateikia išsamią Didžiojo O notacijos apžvalgą, jos reikšmę ir kaip ji gali būti naudojama optimizuoti jūsų kodą globalioms programoms.

Kas yra Didžiojo O notacija?

Didžiojo O notacija yra matematinė notacija, naudojama aprašyti funkcijos ribinį elgesį, kai argumentas siekia tam tikrą reikšmę arba begalybę. Kompiuterių moksle Didysis O naudojamas algoritmams klasifikuoti pagal tai, kaip jų vykdymo laikas arba erdvės reikalavimai auga didėjant įvesties dydžiui. Ji pateikia viršutinę algoritmo sudėtingumo augimo ribą, leidžiančią kūrėjams palyginti skirtingų algoritmų efektyvumą ir pasirinkti tinkamiausią konkrečiai užduočiai.

Pagalvokite apie tai kaip apie būdą apibūdinti, kaip algoritmo veikimas keisis didėjant įvesties dydžiui. Tai ne apie tikslų vykdymo laiką sekundėmis (kuris gali skirtis priklausomai nuo aparatūros), o greičiau apie tempą, kuriuo didėja vykdymo laikas arba erdvės naudojimas.

Kodėl Didžiojo O notacija yra svarbi?

Didžiojo O notacijos supratimas yra gyvybiškai svarbus dėl kelių priežasčių:

Dažniausiai naudojamos Didžiojo O notacijos

Štai keletas dažniausiai naudojamų Didžiojo O notacijų, reitinguojamų nuo geriausio iki blogiausio našumo (pagal laiko sudėtingumą):

Svarbu atsiminti, kad Didžiojo O notacija sutelkia dėmesį į dominuojantį terminą. Žemesniojo laipsnio terminai ir konstanta ignoruojami, nes jie tampa nereikšmingi, kai įvesties dydis tampa labai didelis.

Laiko sudėtingumo ir erdvės sudėtingumo supratimas

Didžiojo O notacija gali būti naudojama analizuoti tiek laiko sudėtingumą, tiek erdvės sudėtingumą.

Kartais galite atsisakyti laiko sudėtingumo erdvės sudėtingumo sąskaita arba atvirkščiai. Pavyzdžiui, galite naudoti maišos lentelę (kuri turi didesnį erdvės sudėtingumą) norėdami paspartinti paieškas (pagerindami laiko sudėtingumą).

Algoritmo sudėtingumo analizė: pavyzdžiai

Pažvelkime į kelis pavyzdžius, kad iliustruotume, kaip analizuoti algoritmo sudėtingumą naudojant Didžiojo O notaciją.

1 pavyzdys: Linijinė paieška (O(n))

Apsvarstykite funkciją, kuri ieško konkrečios reikšmės nerūšiuotame masyve:


function linearSearch(array, target) {
  for (let i = 0; i < array.length; i++) {
    if (array[i] === target) {
      return i; // Rasta tikslinė reikšmė
    }
  }
  return -1; // Tikslinė reikšmė nerasta
}

Blogiausiu atveju (tikslas yra masyvo pabaigoje arba jo nėra), algoritmas turi peržiūrėti visus n masyvo elementus. Todėl laiko sudėtingumas yra O(n), o tai reiškia, kad tam reikia laiko didėja tiesiškai su įvesties dydžiu. Tai gali būti kliento ID paieška duomenų bazės lentelėje, kuri gali būti O(n), jei duomenų struktūra nesuteikia geresnių paieškos galimybių.

2 pavyzdys: Dvejetainė paieška (O(log n))

Dabar apsvarstykite funkciją, kuri ieško reikšmės rūšiuotame masyve naudodama dvejetainę paiešką:


function binarySearch(array, target) {
  let low = 0;
  let high = array.length - 1;

  while (low <= high) {
    let mid = Math.floor((low + high) / 2);

    if (array[mid] === target) {
      return mid; // Rasta tikslinė reikšmė
    } else if (array[mid] < target) {
      low = mid + 1; // Ieškoti dešinėje pusėje
    } else {
      high = mid - 1; // Ieškoti kairėje pusėje
    }
  }

  return -1; // Tikslinė reikšmė nerasta
}

Dvejetainė paieška veikia pakartotinai padalijant paieškos intervalą per pusę. Norint rasti tikslą, reikalingų veiksmų skaičius yra logaritminis įvesties dydžio atžvilgiu. Taigi dvejetainės paieškos laiko sudėtingumas yra O(log n). Pavyzdžiui, žodžio radimas žodyne, kuris yra surūšiuotas pagal abėcėlę. Kiekvienas veiksmas perpus sumažina paieškos erdvę.

3 pavyzdys: Įdėtos kilpos (O(n2))

Apsvarstykite funkciją, kuri palygina kiekvieną masyvo elementą su kiekvienu kitu elementu:


function compareAll(array) {
  for (let i = 0; i < array.length; i++) {
    for (let j = 0; j < array.length; j++) {
      if (i !== j) {
        // Palyginti array[i] ir array[j]
        console.log(`Palyginama ${array[i]} ir ${array[j]}`);
      }
    }
  }
}

Ši funkcija turi įdėtas kilpas, kurių kiekviena iteruoja per n elementų. Todėl bendras operacijų skaičius yra proporcingas n * n = n2. Laiko sudėtingumas yra O(n2). To pavyzdys gali būti algoritmas, skirtas rasti pasikartojančius įrašus duomenų rinkinyje, kur kiekvienas įrašas turi būti lyginamas su visais kitais įrašais. Svarbu suprasti, kad dviejų for kilpų turėjimas savaime nereiškia, kad tai yra O(n^2). Jei kilpos yra viena nuo kitos nepriklausomos, tai yra O(n+m), kur n ir m yra kilpų įvesčių dydžiai.

4 pavyzdys: Pastovus laikas (O(1))

Apsvarstykite funkciją, kuri pasiekia elementą masyve pagal jo indeksą:


function accessElement(array, index) {
  return array[index];
}

Elemento pasiekimas masyve pagal jo indeksą užima tiek pat laiko, neatsižvelgiant į masyvo dydį. Taip yra todėl, kad masyvai siūlo tiesioginę prieigą prie savo elementų. Todėl laiko sudėtingumas yra O(1). Pirmojo masyvo elemento gavimas arba reikšmės gavimas iš maišos žemėlapio naudojant jo raktą yra operacijų su pastoviu laiko sudėtingumu pavyzdžiai. Tai galima palyginti su tikslaus pastato adreso mieste žinojimu (tiesioginė prieiga) arba su kiekvienos gatvės (linijinė paieška) paieška norint rasti pastatą.

Praktinis poveikis globaliam vystymuisi

Didžiojo O notacijos supratimas yra ypač svarbus globaliam vystymuisi, kai programos dažnai turi tvarkyti įvairius ir didelius duomenų rinkinius iš įvairių regionų ir vartotojų bazių.

Patarimai, kaip optimizuoti algoritmo sudėtingumą

Štai keletas praktinių patarimų, kaip optimizuoti jūsų algoritmų sudėtingumą:

Didžiojo O notacijos sukčiavimo lapas

Štai greita nuoroda į dažniausiai pasitaikančias duomenų struktūros operacijas ir jų tipišką Didžiojo O sudėtingumą:

Duomenų struktūra Operacija Vidutinis laiko sudėtingumas Blogiausio atvejo laiko sudėtingumas
Masyvas Prieiga O(1) O(1)
Masyvas Įterpti pabaigoje O(1) O(1) (amortizuotas)
Masyvas Įterpti pradžioje O(n) O(n)
Masyvas Paieška O(n) O(n)
Susietas sąrašas Prieiga O(n) O(n)
Susietas sąrašas Įterpti pradžioje O(1) O(1)
Susietas sąrašas Paieška O(n) O(n)
Maišos lentelė Įterpimas O(1) O(n)
Maišos lentelė Paieška O(1) O(n)
Dvejetainis paieškos medis (subalansuotas) Įterpimas O(log n) O(log n)
Dvejetainis paieškos medis (subalansuotas) Paieška O(log n) O(log n)
Krūva Įterpimas O(log n) O(log n)
Krūva Išskleisti min/maks O(1) O(1)

Be Didžiojo O: kiti našumo aspektai

Nors Didžiojo O notacija suteikia vertingą sistemą algoritmo sudėtingumui analizuoti, svarbu nepamiršti, kad tai ne vienintelis veiksnys, turintis įtakos veikimui. Kiti aspektai yra šie:

Išvada

Didžiojo O notacija yra galingas įrankis algoritmų veikimui suprasti ir analizuoti. Suprasdami Didžiojo O notaciją, kūrėjai gali priimti pagrįstus sprendimus, kokius algoritmus naudoti ir kaip optimizuoti savo kodą dėl skalamumo ir efektyvumo. Tai ypač svarbu globaliam vystymuisi, kai programos dažnai turi tvarkyti didelius ir įvairius duomenų rinkinius. Didžiojo O notacijos įvaldymas yra esminis įgūdis bet kuriam programinės įrangos inžinieriui, norinčiam sukurti didelio našumo programas, kurios gali patenkinti pasaulinės auditorijos poreikius. Sutelkdami dėmesį į algoritmo sudėtingumą ir pasirinkdami tinkamas duomenų struktūras, galite sukurti programinę įrangą, kuri efektyviai masteliuojasi ir suteikia puikią vartotojo patirtį, nepriklausomai nuo jūsų vartotojų bazės dydžio ar vietos. Nepamirškite profiliuoti savo kodo ir kruopščiai išbandyti realiais krūviais, kad patvirtintumėte savo prielaidas ir patobulintumėte savo įgyvendinimą. Prisiminkite, Didysis O yra apie augimo tempą; konstantos vis tiek gali turėti didelį skirtumą praktiškai.