logo

Big O Notation Tutorial - Een gids voor Big O-analyse

Grote O-notatie is een krachtig hulpmiddel dat in de computerwetenschappen wordt gebruikt om de tijdscomplexiteit of ruimtecomplexiteit van algoritmen te beschrijven. Het biedt een gestandaardiseerde manier om de efficiëntie van verschillende algoritmen te vergelijken in termen van hun slechtste prestaties. Begrip Grote O-notatie is essentieel voor het analyseren en ontwerpen van efficiënte algoritmen.

In deze tutorial behandelen we de basisprincipes van Grote O-notatie , de betekenis ervan, en hoe de complexiteit van algoritmen kan worden geanalyseerd Grote O .



Inhoudsopgave

Wat is Big-O-notatie?

Big-O , gewoonlijk aangeduid als Volgorde van , is een manier om de bovengrens van de tijdscomplexiteit van een algoritme, omdat het de het slechtste geval situatie van algoritme. Het biedt een bovengrens op de tijd die een algoritme nodig heeft in termen van de grootte van de invoer. Het wordt aangeduid als O(f(n)) , waar f(n) is een functie die het aantal bewerkingen (stappen) vertegenwoordigt dat een algoritme uitvoert om een ​​probleem van omvang op te lossen N .



Big-O-notatie wordt gebruikt om de prestaties of complexiteit van een algoritme te beschrijven. Concreet beschrijft het de in het slechtste geval aangaande met tijd of complexiteit van de ruimte.

Belangrijk punt:

  • Grote O-notatie beschrijft alleen het asymptotische gedrag van een functie, niet de exacte waarde ervan.
  • De Grote O-notatie kan worden gebruikt om de efficiëntie van verschillende algoritmen of datastructuren te vergelijken.

Definitie van Big-O-notatie:

Gegeven twee functies f(n) En g(n) , dat zeggen wij f(n) is O(g(n)) als er constanten bestaan c> 0 En N 0 >= 0 zodanig dat f(n) <= c*g(n) voor iedereen n>= n 0 .



In eenvoudiger bewoordingen: f(n) is O(g(n)) als f(n) groeit niet sneller dan c*g(n) voor alle n>= n0waarbij c en n0zijn constanten.

Waarom is Big O-notatie belangrijk?

Big O-notatie is een wiskundige notatie die wordt gebruikt om de slechtste tijdscomplexiteit of efficiëntie van een algoritme of de slechtste ruimtecomplexiteit van een datastructuur te beschrijven. Het biedt een manier om de prestaties van verschillende algoritmen en datastructuren te vergelijken, en om te voorspellen hoe ze zich zullen gedragen naarmate de invoer groter wordt.

Big O-notatie is om verschillende redenen belangrijk:

  • Big O Notation is belangrijk omdat het helpt bij het analyseren van de efficiëntie van algoritmen.
  • Het biedt een manier om te beschrijven hoe de looptijd of ruimtevereisten van een algoritme groeit naarmate de invoer groter wordt.
  • Hiermee kunnen programmeurs verschillende algoritmen vergelijken en de meest efficiënte voor een specifiek probleem kiezen.
  • Helpt bij het begrijpen van de schaalbaarheid van algoritmen en het voorspellen van hoe ze zullen presteren naarmate de invoergrootte groeit.
  • Stelt ontwikkelaars in staat code te optimaliseren en de algehele prestaties te verbeteren.

Eigenschappen van Big O-notatie:

Hieronder staan ​​enkele belangrijke eigenschappen van Big O Notation:

1. Reflexiviteit:

Voor elke functie f(n), f(n) = O(f(n)).

Voorbeeld:

f(n) = n2, dan f(n) = O(n2).

2. Transitiviteit:

Als f(n) = O(g(n)) en g(n) = O(h(n)), dan f(n) = O(h(n)).

Voorbeeld:

f(n) = n3, g(n) = n2, h(n) = n4. Dan is f(n) = O(g(n)) en g(n) = O(h(n)). Daarom f(n) = O(h(n)).

3. Constante factor:

Voor elke constante c> 0 en functies f(n) en g(n), als f(n) = O(g(n)), dan cf(n) = O(g(n)).

Voorbeeld:

f(n) = n, g(n) = n2. Dan is f(n) = O(g(n)). Daarom is 2f(n) = O(g(n)).

4. Somregel:

Als f(n) = O(g(n)) en h(n) = O(g(n)), dan f(n) + h(n) = O(g(n)).

Voorbeeld:

f(n) = n2, g(n) = n3, h(n) = n4. Dan is f(n) = O(g(n)) en h(n) = O(g(n)). Daarom f(n) + h(n) = O(g(n)).

5. Productregel:

Als f(n) = O(g(n)) en h(n) = O(k(n)), dan f(n) * h(n) = O(g(n) * k(n)) .

Voorbeeld:

f(n) = n, g(n) = n2, h(n) = n3, k(n) = n4. Dan is f(n) = O(g(n)) en h(n) = O(k(n)). Daarom f(n) * h(n) = O(g(n) * k(n)) = O(n)5).

6. Samenstellingsregel:

Als f(n) = O(g(n)) en g(n) = O(h(n)), dan f(g(n)) = O(h(n)).

Voorbeeld:

f(n) = n2, g(n) = n, h(n) = n3. Dan is f(n) = O(g(n)) en g(n) = O(h(n)). Daarom geldt f(g(n)) = O(h(n)) = O(n).3).

Veel voorkomende Big-O-notaties:

Big-O-notatie is een manier om de tijd- en ruimtecomplexiteit van een algoritme te meten. Het beschrijft de bovengrens van de complexiteit in het worstcasescenario. Laten we eens kijken naar de verschillende soorten tijdcomplexiteit:

1. Lineaire tijdcomplexiteit: Big O(n)-complexiteit

Lineaire tijdscomplexiteit betekent dat de looptijd van een algoritme lineair groeit met de omvang van de invoer.

Neem bijvoorbeeld een algoritme dat doorkruist een array om een ​​specifiek element te vinden :

Codefragment
bool findElement(int arr[], int n, int key) {  for (int i = 0; i < n; i++) {  if (arr[i] == key) {  return true;  }  }  return false; }>

2. Logaritmische tijdscomplexiteit: Big O(log n) complexiteit

Logaritmische tijdscomplexiteit betekent dat de looptijd van een algoritme evenredig is aan de logaritme van de invoergrootte.

string naar geheel getal in Java

Bijvoorbeeld, een binair zoekalgoritme heeft een logaritmische tijdscomplexiteit:

Codefragment
int binarySearch(int arr[], int l, int r, int x) {  if (r>= l) { int midden = l + (r - l) / 2;  als (arr[mid] == x) midden terugkeert;  if (arr[mid]> x) retourneert binarySearch(arr, l, mid - 1, x);  return binarySearch(arr, mid + 1, r, x);  } retourneer -1; }>

3. Kwadratische tijdcomplexiteit: Big O(n2) Complexiteit

Kwadratische tijdscomplexiteit betekent dat de looptijd van een algoritme evenredig is met het kwadraat van de invoergrootte.

Een simpele bijvoorbeeld algoritme voor het sorteren van bellen heeft een kwadratische tijdscomplexiteit:

Codefragment
void bubbleSort(int arr[], int n) {  for (int i = 0; i < n - 1; i++) {  for (int j = 0; j < n - i - 1; j++) {  if (arr[j]>arr[j + 1]) { swap(&arr[j], &arr[j + 1]);  } } } }>

4. Kubieke tijdcomplexiteit: Big O(n3) Complexiteit

Kubieke tijdcomplexiteit betekent dat de looptijd van een algoritme evenredig is aan de kubus van de invoergrootte.

Een naïef bijvoorbeeld algoritme voor matrixvermenigvuldiging heeft een kubieke tijdscomplexiteit:

Codefragment
void multiply(int mat1[][N], int mat2[][N], int res[][N]) {  for (int i = 0; i < N; i++) {  for (int j = 0; j < N; j++) {  res[i][j] = 0;  for (int k = 0; k < N; k++)  res[i][j] += mat1[i][k] * mat2[k][j];  }  } }>

5. Polynomiale tijdscomplexiteit: Big O(nk) Complexiteit

Polynomiale tijdscomplexiteit verwijst naar de tijdscomplexiteit van een algoritme die kan worden uitgedrukt als een polynomiale functie van de invoergrootte N . In Groot O In de notatie wordt gezegd dat een algoritme een polynomiale tijdscomplexiteit heeft als de tijdscomplexiteit dat is Op k ) , waar k is een constante en vertegenwoordigt de graad van de polynoom.

Algoritmen met polynomiale tijdscomplexiteit worden over het algemeen als efficiënt beschouwd, aangezien de looptijd redelijk snel groeit naarmate de invoergrootte toeneemt. Veel voorkomende voorbeelden van algoritmen met polynomiale tijdscomplexiteit zijn onder meer: lineaire tijdcomplexiteit O(n) , kwadratische tijdscomplexiteit O(n 2 ) , En kubieke tijdscomplexiteit O(n 3 ) .

6. Exponentiële tijdscomplexiteit: Big O(2N) Complexiteit

Exponentiële tijdscomplexiteit betekent dat de looptijd van een algoritme verdubbelt bij elke toevoeging aan de invoergegevensset.

Het probleem van bijvoorbeeld het genereren van alle subsets van een set is van exponentiële tijdscomplexiteit:

Codefragment
void generateSubsets(int arr[], int n) {  for (int i = 0; i < (1 << n); i++) {  for (int j = 0; j < n; j++) {  if (i & (1 << j)) {  cout << arr[j] << ' ';  }  }  cout << endl;  } }>

Factoriële tijdcomplexiteit: Big O(n!) Complexiteit

Factoriële tijdscomplexiteit betekent dat de looptijd van een algoritme factorieel groeit met de grootte van de invoer. Dit wordt vaak gezien in algoritmen die alle permutaties van een reeks gegevens genereren.

Hier is een voorbeeld van een algoritme voor factoriële tijdcomplexiteit, dat alle permutaties van een array genereert:

Codefragment
void permute(int* a, int l, int r) {  if (l == r) {  for (int i = 0; i <= r; i++) {  cout << a[i] << ' ';  }  cout << endl;  }  else {  for (int i = l; i <= r; i++) {  swap(a[l], a[i]);  permute(a, l + 1, r);  swap(a[l], a[i]); // backtrack  }  } }>

Als we de meest voorkomende voorbeelden van Big O-notaties zouden plotten, zouden we een grafiek als deze krijgen:

asymtotische analyse

Hoe bepaal je de Big O-notatie?

Grote O-notatie is een wiskundige notatie die wordt gebruikt om de asymptotisch gedrag van een functie naarmate de invoer ervan oneindig groot wordt. Het biedt een manier om de efficiëntie van algoritmen en datastructuren te karakteriseren.

Stappen om de Big O-notatie te bepalen:

1. Identificeer de dominante term:

  • Onderzoek de functie en identificeer de term met de hoogste groeiorde naarmate de invoer groter wordt.
  • Negeer alle constante factoren of termen van lagere orde.

2. Bepaal de volgorde van groei:

  • De volgorde van groei van de dominante term bepaalt de Big O-notatie.

3. Schrijf de Big O-notatie:

  • De Big O-notatie wordt geschreven als O(f(n)), waarbij f(n) de dominante term vertegenwoordigt.
  • Als de dominante term bijvoorbeeld n^2 is, zou de Big O-notatie O(n^2) zijn.

4. Vereenvoudig de notatie (optioneel):

  • In sommige gevallen is de Big O-merk n kan worden vereenvoudigd door constante factoren te verwijderen of door een beknoptere notatie te gebruiken.
  • Bijvoorbeeld, O(2n) kan worden vereenvoudigd tot Op).

Voorbeeld:

Functie: f(n) = 3n3+ 2n2+ 5n + 1

  1. Dominante term: 3n3
  2. Volgorde van groei: Kubisch (n3)
  3. Grote O-notatie: O(n3)
  4. Vereenvoudigde notatie: O(n3)

Wiskundige voorbeelden van runtime-analyse:

Onderstaande tabel illustreert de runtime-analyse van verschillende ordes van algoritmen naarmate de invoergrootte (n) toeneemt.

Nlog(n)Nn * log(n)n^22^nN!
101101010010243628800
twintig2.996twintig59,940010485762.432902e+1818

Algoritmische voorbeelden van runtime-analyse:

De onderstaande tabel categoriseert algoritmen op basis van hun runtime-complexiteit en geeft voorbeelden voor elk type.

TypeNotatieVoorbeeldalgoritmen
LogaritmischO(logboek n)Binaire zoekopdracht
LineairOp)Lineair zoeken
SuperlineairO(n logboek n)Heap sorteren, samenvoegen sorteren
PolynoomO(n^c)Strassen's matrixvermenigvuldiging, bellensortering, selectiesortering, invoegsortering, emmersortering
ExponentieelO(c^n)Toren van Hanoi
FactorieelOp!)Bepalende uitbreiding door minderjarigen, brute kracht Zoekalgoritme voor handelsreizigerprobleem

Algoritmeklassen met aantal bewerkingen en uitvoeringstijd:

Hieronder staan ​​de klassen van algoritmen en hun uitvoeringstijden op een computer die wordt uitgevoerd 1 miljoen handelingen per seconde (1 sec = 10 6 μsec = 10 3 msec) :

Big O-notatieklassen

f(n)

Big O-analyse (aantal bewerkingen) voor n = 10

Uitvoeringstijd (1 instructie/μsec)

constante

O(1)

1

1 µsec

logaritmisch

O(login)

3.32

3 μsec

lineair

Op)

10

10 μsec

O(nlogn)

O(nlogn)

33.2

33 μsec

kwadratisch

Op2)

102

100 μsec

kubieke

Op3)

103

1msec

exponentieel

O(2N)

1024

10 msec

faculteit

Op!)

10!

3,6288 sec

Vergelijking van Big O-notatie, Big Ω (Omega)-notatie en Big θ (Theta)-notatie:

Hieronder staat een tabel waarin de Big O-notatie, Ω (Omega)-notatie en θ (Theta)-notatie worden vergeleken:

NotatieDefinitieUitleg
Grote O (O)f(n) ≤ C * g(n) voor alle n ≥ n0Beschrijft de bovengrens van de looptijd van het algoritme in de het slechtste geval .
Ω (Omega)f(n) ≥ C * g(n) voor alle n ≥ n0Beschrijft de ondergrens van de looptijd van het algoritme in de beste geval .
θ (Theta)C1* g(n) ≤ f(n) ≤ C2* g(n) voor n ≥ n0Beschrijft zowel de boven- als ondergrenzen van het algoritme looptijd .

In elke notatie:

oneindige lus
  • f(n) vertegenwoordigt de functie die wordt geanalyseerd, doorgaans de tijdscomplexiteit van het algoritme.
  • g(n) vertegenwoordigt een specifieke functie die grenst f(n) .
  • C, C1​, En C2 zijn constanten.
  • N 0 ​ is de minimale invoergrootte waarboven de ongelijkheid geldt.

Deze notaties worden gebruikt om algoritmen te analyseren op basis van hun in het slechtste geval (Big O) , beste geval (Ω) , En gemiddeld geval (θ) scenario's.

Veelgestelde vragen over Big O-notatie:

Vraag 1. Wat is Big O-notatie?

Antwoord: Big O Notation is een wiskundige notatie die wordt gebruikt om de bovengrens van de tijdscomplexiteit van een algoritme te beschrijven in termen van hoe deze groeit ten opzichte van de omvang van de invoer.

Vraag 2. Waarom is Big O-notatie belangrijk?

Antwoord: Het helpt ons de efficiëntie van algoritmen te analyseren en te vergelijken door ons te concentreren op het worstcasescenario en te begrijpen hoe hun prestaties schalen met de invoergrootte.

Vraag 3. Hoe wordt Big O Notation berekend?

Antwoord: Big O-notatie wordt bepaald door de dominante bewerking in een algoritme te identificeren en de tijdscomplexiteit ervan uit te drukken in termen van n, waarbij n de invoergrootte vertegenwoordigt.

Vraag 4. Wat betekent O(1) in de Big O-notatie?

Antwoord: O(1) betekent constante tijdscomplexiteit, wat aangeeft dat de uitvoeringstijd van een algoritme niet verandert, ongeacht de invoergrootte.

Vraag 5. Wat is de betekenis van verschillende Big O-complexiteiten zoals O(log n) of O(n^2)?

Antwoord: Verschillende complexiteiten zoals O(log n) of O(n^2) geven weer hoe de prestaties van een algoritme schalen naarmate de invoergrootte toeneemt, wat inzicht geeft in de efficiëntie en schaalbaarheid ervan.

Vraag 6. Kan Big O Notation ook worden toegepast op ruimtecomplexiteit?

Antwoord: Ja, Big O Notation kan ook worden gebruikt om de ruimtecomplexiteit van een algoritme te analyseren en te beschrijven, waarbij wordt aangegeven hoeveel geheugen het nodig heeft in verhouding tot de invoergrootte.

Gerelateerd artikel: