Det dynamisk programmering er en algoritmemodel, der løser et komplekst problem ved at opdele det i underproblemer, gemme deres resultater for at undgå at skulle genberegne disse resultater.
Denne tidsplan bruges, når du har problemer, der kan opdeles i lignende underproblemer, så deres resultater kan genbruges. For det meste bruges denne programmering til optimering.
Før den tilgængelige delproblem løses, forsøger den dynamiske algoritme at undersøge resultaterne af de tidligere løste delproblemer. Subproblemløsninger kombineres for at opnå den bedste løsning.
I stedet for at beregne det samme delproblem igen og igen, kan du gemme din løsning i noget hukommelse, når du først møder dette underproblem. Når det vises igen under løsningen af et andet underproblem, tages den løsning, der allerede er gemt i hukommelsen.
Dette er en vidunderlig idé at rette hukommelsestid, hvor du ved at bruge ekstra plads kan forbedre den tid, der kræves for at finde en løsning..
Artikelindeks
Følgende væsentlige egenskaber er, hvad du skal have et problem med, før dynamisk programmering kan anvendes:
Denne egenskab udtrykker, at et optimeringsproblem kan løses ved at kombinere de optimale løsninger til de sekundære problemer, der udgør det. Disse optimale underkonstruktioner er beskrevet ved rekursion.
For eksempel præsenteres i en graf en optimal understruktur i den korteste sti r, der går fra et toppunkt s til et toppunkt t:
Det vil sige, i denne korteste vej r kan ethvert mellemliggende toppunkt i tages. Hvis r virkelig er den korteste rute, kan den opdeles i underruterne r1 (fra s til i) og r2 (fra i til t) på en sådan måde, at disse igen er de korteste ruter mellem de tilsvarende hjørner.
Derfor, for at finde de korteste ruter, kan løsningen let formuleres rekursivt, hvilket er hvad Floyd-Warshall-algoritmen gør..
Underproblemområdet skal være lille. Det vil sige, at enhver rekursiv algoritme, der løser et problem, bliver nødt til at løse de samme delproblemer igen og igen i stedet for at generere nye delproblemer..
For eksempel for at generere Fibonacci-serien kan denne rekursive formulering overvejes: Fn = F (n-1) + F (n-2), idet man tager som en grundlæggende sag, at F1 = F2 = 1. Så får vi: F33 = F32 + F31 og F32 = F31 + F30.
Som du kan se, bliver F31 løst i de rekursive undertræer for både F33 og F32. Selvom det samlede antal underproblemer er meget lille, vil du ende med at løse de samme problemer igen og igen, hvis du vedtager en rekursiv løsning som denne..
Dette tages i betragtning ved dynamisk programmering, så det løser kun hvert underproblem én gang. Dette kan opnås på to måder:
Hvis løsningen på ethvert problem kan formuleres rekursivt ved hjælp af løsningen på dets underproblemer, og hvis disse underproblemer overlapper hinanden, kan løsningerne på delproblemerne let huskes eller gemmes i en tabel.
Hver gang der søges efter en ny delproblemløsning, kontrolleres tabellen for at se, om den tidligere var løst. Hvis en opløsning er gemt, vil den blive brugt i stedet for at beregne den igen. Ellers løses underproblemet og gemmer løsningen i tabellen.
Når løsningen på et problem er formuleret rekursivt med hensyn til dets delproblemer, vil det være muligt at forsøge at omformulere problemet på en stigende måde: først vil vi forsøge at løse delproblemerne og bruge deres løsninger til at nå frem til løsninger på de større delproblemer.
Dette gøres også generelt i tabelform, hvilket iterativt genererer løsninger på større og større delproblemer ved at bruge løsninger på mindre delproblemer. For eksempel, hvis værdierne for F31 og F30 allerede er kendt, kan værdien af F32 beregnes direkte.
Et væsentligt træk ved et problem, der kan løses dynamisk, er at det skal have underproblemer, der overlapper hinanden. Dette er det, der adskiller dynamisk programmering fra delings- og erobringsteknikken, hvor det ikke er nødvendigt at gemme de enkleste værdier.
Det svarer til rekursion, da den endelige værdi ved beregning af basissagerne kan bestemmes induktivt. Denne bottom-up-tilgang fungerer godt, når en ny værdi kun afhænger af tidligere beregnede værdier.
For ethvert positivt heltal "e" kan ethvert af de følgende tre trin udføres.
- Træk 1 fra nummeret. (e = e-1).
- Hvis det er deleligt med 2, divideres med 2 (hvis e% 2 == 0, så e = e / 2).
- Hvis det er deleligt med 3, divideres med 3 (hvis e% 3 == 0, så e = e / 3).
Baseret på ovenstående trin skal minimumsantalet af disse trin findes for at bringe e til 1. For eksempel:
- Hvis e = 1, resultat: 0.
- Hvis e = 4, resultat: 2 (4/2 = 2/2 = 1).
- Når e = 7, resultat: 3 (7-1 = 6/3 = 2/2 = 1).
Man kan tænke på altid at vælge det trin, der gør n så lavt som muligt og fortsætte sådan, indtil det når 1. Men det kan ses, at denne strategi ikke fungerer her..
For eksempel, hvis e = 10, vil trinnene være: 10/2 = 5-1 = 4/2 = 2/2 = 1 (4 trin). Den optimale form er dog: 10-1 = 9/3 = 3/3 = 1 (3 trin). Derfor skal alle mulige trin, der kan udføres for hver fundet n værdi, prøves, idet man vælger det mindste antal af disse muligheder.
Alt starter med rekursion: F (e) = 1 + min F (e-1), F (e / 2), F (e / 3) hvis e> 1, idet der tages udgangspunkt: F (1) = 0. Når vi har gentagelsesligningen, kan vi begynde at kode rekursionen.
Det kan dog ses, at det har overlappende delproblemer. Desuden afhænger den optimale løsning for et givet input af den optimale løsning af dens underproblemer.
Som i huskningen, hvor løsningerne på de underproblemer, der løses, opbevares til senere brug. Eller som ved dynamisk programmering starter du i bunden og arbejder dig op til den givne e. Så begge koder:
En af de største fordele ved at bruge dynamisk programmering er, at den fremskynder behandlingen, da der tidligere er beregnet referencer. Da det er en rekursiv programmeringsteknik, reducerer det programmets kodelinjer.
Grådige algoritmer ligner dynamisk programmering, idet de begge er værktøjer til optimering. Den grådige algoritme ser dog efter en optimal løsning ved hvert lokale trin. Det vil sige, det søger et grådigt valg i håb om at finde et globalt optimalt..
Derfor kan grådige algoritmer antage, at det ser optimalt ud på det tidspunkt, men som bliver dyrt i fremtiden og ikke garanterer et globalt optimalt..
På den anden side finder dynamisk programmering den optimale løsning for underproblemerne og træffer derefter et informeret valg ved at kombinere resultaterne af disse underproblemer for faktisk at finde den mest optimale løsning..
- Der er brug for meget hukommelse til at gemme det beregnede resultat af hvert underproblem, hvilket ikke kan garantere, at den lagrede værdi vil blive brugt eller ej.
- Mange gange gemmes outputværdien uden nogensinde at blive brugt i de følgende underproblemer under udførelsen. Dette fører til unødvendig hukommelsesforbrug.
- I dynamisk programmering kaldes funktioner rekursivt. Dette holder stakhukommelsen konstant stigende.
Hvis du har begrænset hukommelse til at køre din kode, og behandlingshastighed ikke er et problem, kan du bruge rekursion. For eksempel, hvis du udvikler en mobilapplikation, er hukommelsen meget begrænset til at køre applikationen.
Hvis du vil have, at programmet kører hurtigere, og du ikke har hukommelsesbegrænsninger, foretrækkes det at bruge dynamisk programmering.
Dynamisk programmering er en effektiv metode til løsning af problemer, der ellers kan synes ekstremt vanskelige at løse på en rimelig tid..
Algoritmer baseret på det dynamiske programmeringsparadigme bruges i mange videnskabelige områder, herunder mange eksempler inden for kunstig intelligens, fra planlægning af problemløsning til talegenkendelse..
Dynamisk programmering er ret effektiv og fungerer meget godt til en lang række problemer. Mange algoritmer kan ses som grådige algoritmeapplikationer, såsom:
- Fibonacci-nummerserie.
- Hanoi Towers.
- Alle korteste rutepar af Floyd-Warshall.
- Problem med rygsæk.
- Projektplanlægning.
- Den korteste vej gennem Dijkstra.
- Flyvekontrol og robotik kontrol.
- Matematiske optimeringsproblemer.
- Tidsdeling - Planlæg jobbet for at maksimere CPU-brugen.
Fibonacci-tal er tallene, der findes i følgende rækkefølge: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 osv..
I matematisk terminologi er sekvensen Fn for Fibonacci-tal defineret ved gentagelsesformlen: F (n) = F (n -1) + F (n -2), hvor F (0) = 0 og F (1) = 1.
I dette eksempel initialiseres et søgearray med alle startværdier med -1. Når der er brug for løsningen på et underproblem, søges der i denne søgematrix først.
Hvis den beregnede værdi er der, returneres denne værdi. Ellers beregnes resultatet for at blive gemt i søgearrayet, så det kan genbruges senere.
I dette tilfælde beregnes f (0) for den samme Fibonacci-serie først, derefter f (1), f (2), f (3) osv. Løsningerne på delproblemerne konstrueres således fra bunden op.
Endnu ingen kommentarer