Gyakorlati Java példák a nagy O jelölésre

1. Áttekintés

Ebben az oktatóanyagban arról fogunk beszélni, hogy mi A Big O jelölés azt jelenti. Néhány példát mutatunk be, hogy megvizsgáljuk a kód futási idejére gyakorolt ​​hatását.

2. A nagy O jelölés intuíciója

Gyakran halljuk a a Big O Notation segítségével leírt algoritmus teljesítménye.

Az algoritmusok - vagy algoritmikus komplexitás - teljesítményének vizsgálata az algoritmuselemzés területére esik. Az algoritmuselemzés arra a kérdésre ad választ, hogy egy algoritmus hány erőforrást - például lemezterületet vagy időt - használ fel.

Az időt erőforrásként fogjuk vizsgálni. Általában minél kevesebb idő telik el egy algoritmus elkészítéséhez, annál jobb.

3. Állandó idejű algoritmusok - O (1)

Hogyan befolyásolja az algoritmus ez a bemeneti mérete a futási idejét? A Big O megértésének kulcsa a dolgok növekedési sebességének megértése. A kérdéses arány itt bemeneti méretenként vett idő.

Fontolja meg ezt az egyszerű kóddarabot:

int n = 1000; System.out.println ("Hé - a bemenet:" + n);

Egyértelmű, hogy nem mindegy, mi n felül van. Ennek a kódrésznek a futtatása állandó ideig tart. Ez nem függ az n méretétől.

Hasonlóképpen:

int n = 1000; System.out.println ("Hé - a bemenet:" + n); System.out.println ("Hmm .. Több dolgot csinálok a következővel:" + n); System.out.println ("És még sok más:" + n);

A fenti példa szintén állandó idő. Még akkor is, ha háromszor annyi időbe telik a futás nem függ a bemenet méretétől, n. Az állandó idejű algoritmusokat a következőképpen jelöljük: O (1). Vegye figyelembe, hogy O (2), O (3) vagy akár O (1000) ugyanazt jelentené.

Nem érdekel, hogy pontosan mennyi idő alatt futunk, csak az, hogy állandó időbe telik.

4. Logaritmikus idő algoritmusok - O (log n)

Az állandó idejű algoritmusok (aszimptotikusan) a leggyorsabbak. A logaritmikus idő a következő leggyorsabb. Sajnos kissé trükkösebbek elképzelni őket.

A logaritmikus időalgoritmus egyik gyakori példája a bináris keresési algoritmus. Kattintson ide a bináris keresés Java-ban történő megvalósításának megtekintéséhez.

Ami itt fontos, hogy a a futási idő arányosan növekszik a bemenet logaritmusával (ebben az esetben jelentkezzen be a 2. bázisra):

for (int i = 1; i <n; i = i * 2) {System.out.println ("Hé - elfoglalt vagyok, ha ránézek:" + i); }

Ha n értéke 8, a kimenet a következő lesz:

Hé - elfoglalt a nézés: 1 Hé - elfoglalt a nézés: 2 hé - elfoglalt a nézés: 4

Egyszerű algoritmusunk log (8) = 3-szor futott.

5. Lineáris idő algoritmusok - Tovább)

A logaritmikus időalgoritmusok után megkapjuk a következő leggyorsabb osztályt: lineáris idő algoritmusok.

Ha azt mondjuk, hogy valami lineárisan növekszik, akkor azt értjük, hogy közvetlenül arányos a bemeneteinek méretével.

Gondolj egy egyszerű ciklusra:

for (int i = 0; i <n; i ++) {System.out.println ("Hé - elfoglalt vagyok, ha ránézek:" + i); }

Hányszor fut ez a huroknál? n természetesen, természetesen! Nem tudjuk pontosan, hogy ez mennyi időbe telik, és ez nem aggódik.

Amit tudunk, az az, hogy a fent bemutatott egyszerű algoritmus lineárisan növekszik a bemenet nagyságával.

Jobban szeretnénk egy futási időt 0,1n mint (1000n + 1000), de mindkettő továbbra is lineáris algoritmus; mindkettő közvetlenül nő a ráfordítások méretével arányosan.

Ismételten, ha az algoritmust a következőkre változtattuk:

for (int i = 0; i <n; i ++) {System.out.println ("Hé - elfoglalt vagyok, ha ránézek:" + i); System.out.println ("Hmm .. Nézzük meg még egyszer:" + i); System.out.println ("És még egy:" + i); }

A futásideje továbbra is lineáris lenne a bemeneti méretben, n. A lineáris algoritmusokat a következőképpen jelöljük: Tovább).

Csakúgy, mint a konstans idejű algoritmusok esetében, itt sem törődünk a futási idő sajátosságaival. O (2n + 1) ugyanaz mint Tovább), mivel a Big O Notation a bemeneti méret növekedésével foglalkozik.

6. N Log N idő algoritmusok - O (n log n)

n log n az algoritmusok következő osztálya. A futási idő arányosan nő n log n a bemenet

for (int i = 1; i <= n; i ++) {for (int j = 1; j <n; j = j * 2) {System.out.println ("Hé, elfoglalt vagyok, ha megnézem:" + i + "és" + j); }} 

Például, ha a n értéke 8, akkor ez az algoritmus futni fog 8 * log (8) = 8 * 3 = 24 alkalommal. Az, hogy szigorú egyenlőtlenségünk van-e vagy sem a for ciklusban, a Big O jelölés szempontjából lényegtelen.

7. Polinomiális idő algoritmusok - O (np)

Ezután polinomiális idő algoritmusokat kapunk. Ezek az algoritmusok még lassabbak, mint n log n algoritmusok.

A polinom kifejezés általános kifejezés, amely másodfokú (n2), köbös (n3), kvartikus (n4)stb funkciók. Amit fontos tudni, az az O (n2) gyorsabb, mint O (n3) ami gyorsabb, mint O (n4)stb.

Vessünk egy pillantást a másodfokú idő algoritmus egyszerű példájára:

for (int i = 1; i <= n; i ++) {for (int j = 1; j <= n; j ++) {System.out.println ("Hé - elfoglalt a nézés:" + i + "és" + j); }} 

Ez az algoritmus futni fog 82 = 64 alkalommal. Megjegyezzük, hogy ha egy másikat fészkelnénk a hurokhoz, akkor ez egy O (n3) algoritmus.

8. Exponenciális idő algoritmusok - O (kn)

Most veszélyes területre kerülünk; ezek az algoritmusok a bemeneti méret által hatványozott tényezővel arányosan nőnek.

Például, O (2n) az algoritmusok minden további bemenettel megduplázódnak. Tehát, ha n = 2, ezek az algoritmusok négyszer fognak futni; ha n = 3, nyolcszor fognak futni (olyan, mint a logaritmikus időalgoritmusok ellentéte).

O (3n) algoritmusok megháromszorozódnak minden további bemenetnél, O (kn) az algoritmusok minden további bemenet esetén k-szor nagyobbak lesznek.

Vessünk egy pillantást egy egyszerű példára O (2n) idő algoritmus:

for (int i = 1; i <= Math.pow (2, n); i ++) {System.out.println ("Hé - elfoglalt vagyok, ha ránézek:" + i); }

Ez az algoritmus futni fog 28 = 256 alkalommal.

9. Faktoriális idő algoritmusok - Tovább!)

A legtöbb esetben ez nagyjából olyan rossz, mint amilyenné válik. Ennek az algoritmusosztálynak a futási ideje arányos a bemeneti méret tényezőjével.

Klasszikus példa erre az utazó eladó problémájának megoldása, egy durva erő megközelítésével annak megoldására.

Az utazó eladó problémájának megoldásának magyarázata meghaladja a cikk kereteit.

Nézzünk inkább egy egyszerűt Tovább!) algoritmus, az előző szakaszokhoz hasonlóan:

mert (int i = 1; i <= faktoriális (n); i ++) {System.out.println ("Hé - elfoglalt vagyok, ha ránézek:" + i); }

hol tényező (n) egyszerűen kiszámítja az n-t. Ha n értéke 8, akkor ez az algoritmus futni fog 8! = 40320 alkalommal.

10. Aszimptotikus funkciók

A Big O az úgynevezett an aszimptotikus funkció. Mindez azt jelenti, hogy egy algoritmus teljesítményével foglalkozik a határon - azaz - amikor sok bemenet dobódik rá.

A Big O nem törődik azzal, hogy az algoritmusod milyen jól működik a kis méretű bemenetekkel. Nagy bemenetekkel foglalkozik (gondoljon arra, hogy válogasson egymillió számot, vagy válasszon 5 számot).

Egy másik dolog, amit meg kell jegyezni, az vannak más aszimptotikus funkciók. A Big Θ (theta) és Big Ω (omega) szintén leírja az algoritmusokat a határértéknél (ne feledje, a határ ez csak azt jelenti, hogy hatalmas bemenetekre van szükség).

A 3 fontos funkció közötti különbségek megértéséhez először tudnunk kell, hogy a Big O, a Big Θ és a Big Ω mindegyike leír egy készlet (azaz elemek gyűjteménye).

Itt halmazaink tagjai maguk algoritmusok:

  • A nagy O az összes futó algoritmus halmazát írja le nem rosszabb mint egy bizonyos sebesség (ez egy felső határ)
  • Ezzel szemben a Big Ω leírja az összes futó algoritmus halmazát nem jobb mint egy bizonyos sebesség (ez egy alsó határ)
  • Végül Big Θ leírja az összes futó algoritmus halmazát nál nél egy bizonyos sebesség (olyan, mint az egyenlőség)

A fenti fogalommeghatározások matematikailag nem pontosak, de segítenek megértésünkben.

Általában a Big O segítségével leírt dolgokat hallja, de nem árt tudni a Big Θ-ról és a Big Ω-ról.

11. Következtetés

Ebben a cikkben megvitattuk a Big O jelölést és hogyan Az algoritmus bonyolultságának megértése befolyásolhatja a kód futási idejét.

A különböző bonyolultsági osztályok nagyszerű megjelenítését itt találja.

Szokás szerint ennek az oktatóanyagnak a kódrészletei megtalálhatók a GitHubon.