Dünaamilise programmeerimise kiire rakendamine

Algoritmide uurimisel oleme tulemuste saamiseks rakendanud paljusid tehnikaid. Mõnes kontseptsioonis on kasutatud iOS-spetsiifilisi mustreid, teised aga üldisemalt. Ehkki seda pole sõnaselgelt mainitud, on mõned meie lahendused kasutanud konkreetset programmeerimisstiili, mida nimetatakse dünaamiliseks programmeerimiseks. Ehkki teoreetiliselt lihtne, võib selle kohaldamine mõnikord olla nüansiline. Õige rakenduse korral võib dünaamilisel programmeerimisel olla koodi kirjutamisele võimas mõju. Selles essees tutvustame dünaamilise programmeerimise kontseptsiooni ja rakendamist.

Hoia hilisemaks

Kui olete midagi Amazon.com-i kaudu ostnud, on teil tuttav saidi termin - „Salvesta hilisemaks”. Nagu fraasist nähtub, antakse ostjatele võimalus lisada tooteid ostukorvi või salvestada need soovide nimekirja hilisemaks vaatamiseks. Algoritmide kirjutamisel seisame sageli silmitsi sarnase valikuga toimingute lõpuleviimisel (arvutuste tegemisel), kuna andmeid tõlgendatakse või salvestatakse tulemusi hilisemaks kasutamiseks. Näideteks on JSON-i andmete hankimine RESTful-teenusest või Core Data Framework'i kasutamine:

IOS-is saavad kujundusmustrid aidata meil aega ja andmete töötlemise koordineerimist. Spetsiifilised tehnikad hõlmavad mitme keermega toiminguid (nt Grand Central Dispatch), teatisi ja volitusi. Dünaamiline programmeerimine (DP) ei ole seevastu tingimata ühtne kodeerimise tehnika, vaid pigem see, kuidas mõelda toiminguna toimuvatele toimingutele (nt alamprobleemid). Saadud DP-lahendus võib sõltuvalt probleemist erineda. Algoritmi efektiivsuse suurendamiseks tugineb dünaamiline programmeerimine kõige lihtsamal kujul andmete salvestamisele ja taaskasutamisele. Andmete taaskasutamise protsessi nimetatakse ka meeldejätmiseks ja see võib esineda mitmel kujul. Nagu näeme, pakub selline programmeerimisstiil mitmeid eeliseid.

Fibonacci vaadati üle

Rekursiooni käsitlevas essees võrdlesime massiivi väärtuste klassikalise järjestuse loomist, kasutades nii iteratiivset kui ka rekursiivset tehnikat. Nagu arutatud, olid need algoritmid kavandatud massiivi jada tootmiseks, mitte konkreetse tulemuse arvutamiseks. Võttes seda arvesse, saame luua ühe sisemise väärtuse saamiseks Fibonacci uue versiooni:

func fibRecursive (n: Int) -> Int {
    kui n == 0 {
        tagasi 0
    }
    
    kui n <= 2 {
        tagasi 1
    }
    
    tagasi fibRecursive (n: n-1) + fibRecursive (n: n-2)
}

Esmapilgul näib, et see näiliselt väike funktsioon oleks ka tõhus. Edasisel analüüsimisel näeme siiski, et selle tulemuse arvutamiseks tuleb teha arvukalt rekursiivseid kõnesid. Nagu allpool näidatud, kuna fibRecursive ei saa salvestada varem arvutatud väärtusi, suurenevad selle rekursiivsed kõned plahvatuslikult:

Fibonacci meelde jäetud

Proovime teist tehnikat. Paigutatud Swifti funktsioonina fikseerib fibMemoized massiivi tagasiväärtuse oma alamfunktsioonist fibSequence, et arvutada lõplik väärtus:

laiendus Int {
    
    // meeldejääv versioon
    muteeruv func fibMemoized () -> Int {
        
        // ehitab massiivi jada
        func fibSequence (_ jada: massiiv  = [0, 1]) -> massiiv  {
            
            var lõplik = massiiv  ()
            
            // muteeritud koopia
            var väljund = jada
            
            las i: Int = output.count
            
            // seatud põhitingimus - lineaarne aeg O (n)
            kui ma == ise {
                tagasi väljund
            }
            
            las tulemused: Int = väljund [i - 1] + väljund [i - 2]
            output.append (tulemused)
            
            // määra iteratsioon
            lõplik = fibSequence (väljund)
            naasta finaali
            
        }
        
        
        // arvutage lõpptoode - konstantse ajaga O (1)
        las tulemused = fibSequence ()
        andke vastus: Int = tulemused [results.endIndex - 1] + results [results.endIndex - 2]
        vastuse tagastamine
        
    }
}

Isegi kui fibSquence sisaldab rekursiivset jada, määratakse selle baasjuhtum soovitud massiivi positsioonide arvuga (n). Toimivuse mõttes öeldakse, et fibSequence jookseb lineaarses ajas või O (n). See jõudluse parandamine saavutatakse lõpptoote arvutamiseks vajaliku massiivi järjestuse meeldejätmisega. Selle tulemusel arvutatakse iga jada permutatsioon üks kord. Selle tehnika eelis on nähtav kahe allpool toodud algoritmi võrdlemisel:

Lühemad teed

Koodimälu võib parandada ka programmi tõhusust nii, et näiliselt keerulised või peaaegu lahendamatud küsimused saavad vastutusele võtta. Selle näite võib leida Dijkstra algoritmist ja lühimatest radadest. Läbivaatamiseks lõime ainulaadse andmestruktuuri nimega Path eesmärgiga säilitada konkreetsed läbipääsu metaandmed:

// rajaklass hoiab objekte, mis koosnevad piirist
klassi tee {
    
    var kokku: Int
    var sihtkoht: Vertex
    var eelmine: tee?
   // objekti initsialiseerimine
    selles(){
        sihtkoht = Vertex ()
        kokku = 0
    }
}

Tee teeb kasulikuks selle võime salvestada andmeid varem külastatud sõlmedesse. Sarnaselt meie muudetud Fibonacci algoritmile salvestab Path kõigi kumulatiivsete servade kaalu kõigi läbitud tippude (kokku) ning iga külastatud Vertexi täieliku ajaloo. Tõhusalt kasutatuna võimaldab see programmeerijal vastata küsimustele, näiteks Vertexi sihtkohta sihtkohta navigeerimise keerukusele, kui läbimine oli tõepoolest edukas (sihtkoha leidmine), samuti kogu külastatud sõlmede loendile. Sõltuvalt graafiku suurusest ja keerukusest võib selle teabe puudumine tähendada seda, et algoritmi andmete (ümber) arvutamine võtab nii kaua aega, et see muutub efektiivseks liiga aeglaseks või ei suuda ebapiisavate andmete tõttu elutähtsaid küsimusi lahendada.

Kas teile meeldis see essee? Lugege ja avastage minu muud sisu meediumil või hankige kogu raamat EPUB-, PDF- või Kindle-vormingus.