Maximális Subarray probléma a Java-ban
1. Áttekintés
A maximális alray probléma egy olyan feladat, hogy megtalálja a szomszédos elemek sorozatát a maximális összeggel az adott tömbben.
Például az alábbi tömbben a kiemelt részrajz maximális összege (6):

Ebben az oktatóanyagban két megoldást fogunk megvizsgálni a tömb maximális részterületének megtalálásához. Az egyiket megtervezzük Tovább) idő és tér összetettsége.
2. Brute Force algoritmus
A nyers erő iteratív megközelítés a probléma megoldására. A legtöbb esetben a megoldás többszörös ismétlést igényel egy adatszerkezet felett. A következő néhány szakaszban ezt a megközelítést alkalmazzuk a maximális részarakter probléma megoldására.
2.1. Megközelítés
Általánosságban elmondható, hogy az első megoldás, ami eszembe jut, az, hogy kiszámoljuk az összes lehetséges részarány összegét, és visszaadjuk azt, amelynek a maximális összege van.
Először kiszámoljuk az összes részarány összegét, amely a 0 indexen kezdődik. És hasonlóan, meg fogjuk találni az összes alsávot minden indextől kezdve 0 nak nek n-1 hol n a tömb hossza:

Tehát az indexnél kezdjük 0 és adjon hozzá minden elemet az iteráció futó összegéhez. Szintén jól tartsa nyilván az eddig látott maximális összeget. Ez az iteráció a fenti kép bal oldalán látható.
A kép jobb oldalán láthatjuk az indexről induló iterációt 3. A kép utolsó részében megkapjuk az alrészt az index közötti maximális összeggel 3 és 6.
Azonban, algoritmusunk továbbra is megtalálja az összes alsávot a között lévő indexektől kezdve 0 és n-1.
2.2. Végrehajtás
Most nézzük meg, hogyan tudjuk megvalósítani ezt a megoldást a Java-ban:
public int maxSubArray (int [] nums) {int n = nums.hossz; int maximumSubArraySum = Egész.MIN_VALUE; int kezdet = 0; int vége = 0; mert (int bal = 0; bal <n; bal ++) {int runningWindowSum = 0; for (int jobb = bal; jobb maximumSubArraySum) {maximumSubArraySum = runningWindowSum; start = bal; vég = jobb; }}} logger.info ("Megtalált maximális alrész {} és {} között, kezdet, vég); return maximumSubArraySum; }
A várakozásoknak megfelelően frissítjük a maximumSubArraySum ha az aktuális összeg meghaladja az előző maximális összeget. Nevezetesen, ezután frissítjük a Rajt és vége hogy megtudja ennek az alrésznek az index helyeit.
2.3. Bonyolultság
Általánosságban elmondható, hogy a nyers erő megoldása sokszor ismétlődik a tömbön, hogy minden lehetséges megoldást megkapjon. Ez azt jelenti, hogy a megoldás által igénybe vett idő kvadratikusan növekszik a tömbben lévő elemek számával együtt. Ez nem jelenthet problémát kis méretű tömbök esetén. De a tömb méretének növekedésével ez a megoldás nem hatékony.
A kód megvizsgálásával azt is láthatjuk, hogy két beágyazott van mert hurkok. Ezért arra a következtetésre juthatunk, hogy ennek az algoritmusnak az időbeli összetettsége igen O (n2).
A későbbi szakaszokban ezt a problémát itt oldjuk meg Tovább) komplexitás dinamikus programozással.
3. Dinamikus programozás
A dinamikus programozás megoldja a problémát azáltal, hogy kisebb részproblémákra osztja. Ez nagyon hasonlít az osztani és meghódítani algoritmus-megoldási technikára. A fő különbség azonban az, hogy a dinamikus programozás csak egyszer oldja meg a részproblémát.
Ezután eltárolja ennek az alproblémának az eredményét, majd később felhasználja ezt az eredményt más kapcsolódó részproblémák megoldására. Ezt a folyamatot memoizálásnak nevezik.
3.1. Kadane algoritmusa
Kadane algoritmusa népszerű megoldás a maximális részarakter problémára, és ez a megoldás dinamikus programozáson alapul.
A dinamikus programozás megoldásának legfontosabb kihívása probléma az optimális részproblémák megtalálása.
3.2. Megközelítés
Értsük meg ezt a problémát másképp:

A fenti képen feltételezzük, hogy a maximális részrajz az utolsó index helyén ér véget. Ezért az alrész maximális összege a következő lesz:
maximumSubArraySum = max_so_far + arr [n-1]
max_so_far az indexen végződő részterület maximális összege n-2. Ezt mutatja a fenti kép is.
Ezt a feltételezést alkalmazhatjuk a tömb bármely indexére. Például a maximális részösszeg, amelynek vége: n-2 kiszámítható:
maximumSubArraySum [n-2] = max_so_far [n-3] + arr [n-2]
Ezért arra a következtetésre juthatunk, hogy:
maximumSubArraySum [i] = maximumSubArraySum [i-1] + arr [i]
Most, mivel a tömbben minden elem egy speciális méretű alrészlet, azt is ellenőriznünk kell, hogy egy elem nagyobb-e, mint maga a maximális összeg:
maximumSubArraySum [i] = Max (arr [i], maximumSubArraySum [i-1] + arr [i])
Ezeket az egyenleteket megnézve láthatjuk, hogy a tömb minden indexénél meg kell találnunk a maximális részösszetétel összegét. Így felosztottuk a problémánkat n részproblémák. Megtalálhatjuk a maximális összeget minden indexnél, ha a tömböt csak egyszer iteráljuk:

A kiemelt elem az iteráció aktuális elemét mutatja. Minden indexnél a korábban kapott egyenletet alkalmazzuk az érték kiszámításához max_ending_here. Ez segít nekünk az azonosításban be kell-e vonni az aktuális elemet az alrészbe, vagy ebből az indexből kiindulva indítunk-e új alrészt.
Egy másik változó, max_so_far az iteráció során talált maximális részösszeg tárolására szolgál. Amint iterálunk az utolsó index felett, max_so_far a maximális részarány összegét tárolja.
3.3. Végrehajtás
Ismét lássuk, hogyan tudjuk most megvalósítani a Kadane algoritmust a Java-ban, a fenti megközelítést követve:
public int maxSubArraySum (int [] arr) {int méret = arr.length; int kezdet = 0; int vége = 0; int maxSoFar = 0, maxEndingHere = 0; for (int i = 0; i maxEnding Here + arr [i]) {start = i; maxEndingHere = arr [i]; } else maxEndingHere = maxEndingHere + arr [i]; if (maxSoFar <maxEndingHere) {maxSoFar = maxEndingHere; vég = i; }} logger.info ("Megtalált maximális részrajz {} és {} között", kezdet, vég); return maxSoFar; }
Itt frissítettük a Rajt és vége hogy megtalálja a maximális részrajzi indexeket.
3.4. Bonyolultság
Mivel csak egyszer kell iterálnunk a tömböt, ennek az algoritmusnak az időbeli összetettsége igen Tovább).
Tehát, mint láthatjuk, ennek a megoldásnak az ideje lineárisan növekszik a tömb elemeinek számával. Következésképpen hatékonyabb, mint az utolsó szakaszban tárgyalt durva erő megközelítés.
4. Következtetés
Ebben a gyors bemutatóban két módszert írtunk le a maximális alrét probléma megoldására.
Először egy nyers erő megközelítést tártunk fel, és láttuk, hogy ez az iteratív megoldás másodfokú időt eredményezett. Később aztán megvitattuk a Kadane algoritmust, és dinamikus programozással oldottuk meg a problémát lineáris időben.
Mint mindig, a cikk teljes forráskódja elérhető a GitHubon.