Programazio dinamiko

Informatikan, programazio dinamikoa algoritmo baten exekuzio denbora murrizteko metodoa da. Horretarako azpi-problema gainjarriak eta azpi-egitura optimoak erabiltzen ditu.

Richard Bellman matematikariak programazio dinamikoa asmatu zuen 1953. urtean, problema konplexuak optimizatzeko, hauek sekuentzializatuz eta diskretatuz.

Sarrera

Azpi-egitura optimo bat, azpi-problemen konponbide optimoak erabili daitezkela esan nahi du, problemaren soluzio optimoa bere osotasunean topatzeko.

Adibidez, grafo baten bi erpinen arteko biderik laburrena kalkulatu daiteke, hasierakoaren aldameneko erpin guztietatik amaiera arte bide laburrena kalkulatuz eta ondoren, emaitza hauek baliatu biderik hoberena aukeratzeko. Oro har, azpi-egitura optimoak erabiliz, problema guztiak ebatzi daitezke hiru pausu hauek jarraituz:

  • Problema azpi-problema txikiagotan zatitu.
  • Problema hauek era optimoan ebatzi, hiru pausuetako prozesu hau jarraituz.
  • Emaitza optimo hauek erabili, problema nagusiaren soluzio optimoa eraikitzeko.

Azpi-problemak azpi-problemetan zatituz ebazten dira kasu errazera ailegatu arte, non emaitza tribiala den.

Problema batek azpi-problema gainjarriak dituela esatea, problema nagusiak ebazteko azpi-problema bera erabiltzen dela esatea da. Adibidez, Fibonacciren zenbakien segidan (F3 = F1 + F2 y F4 = F2 + F3) , F3 eta F4 kalkulatzeko, F2 erabili behar da. F5 kalkulatu nahi badugu, erabili F3 eta F4 beharko ditugu. F5en inplementazio txar batek, F2 bi aldiz edo gehiago kalkulatuko du. Hau beti gertatuko da azpi-problema gainjarriak ditugunean, non algoritmoaren exekuzio denbora igoko den.

Hau ekiditeko kalkulatu ditugun soluzioak zerrenda batean gorde daitezke. Beraz, problema bera berriz ebatzi nahi bada, lehen kalkulatu dugun soluzioa zerrendan bilatu dezakegu, hau berrerabiltzeko (memorizazioa).

Laburbilduz, programazio dinamikoak hauek erabiltzen ditu:

  • Azpi-problema gainjarriak
  • Azpi-egitura optimoak
  • Memorizazioa

Gainera, bi planteamendu ezberdin azpimarra ditzakegu:

  • Top-down: Problema azpi-problemetan zatitzen da, eta hauek soluzioak gogoratuz ebazten dira.
  • Bottom-up: Ahal diren problema guztian aldez aurretik ebazten dira, eta ondoren, problema nagusiak ebazteko erabiltzen dira.

Programazio funtzional lengoaia batzuk, Haskell batez ere, memorizazioa erabil dezake automatikoki, prozesuak azkartzeko.

Adibidea

Fibonacciren zenbakiak:

Segida matematiko hau errekurtsio honekin adierazi daiteke:

Fibonacciren segidan n posizioan dagoen zenbakia topatzeko, honako programa errekurtsiboa sor dezakegu, kostu exponentziala eukiko duena:

FUNC fib(↓n: ARRUNTA): ARRUNTA
HASIERA
   BALDIN n = 0 ORDUAN
       ITZULI 0
   BESTELA, BALDIN n = 1 ORDUAN
       ITZULI 1
   BESTELA
       ITZULI fib(n-1) + fib(n-2)
   BALDIN AMAITU
AMAITU

Demagun fib(5) deitzen dugula, orduan zuhaitz dei bat eratuko genuke non parametro berdinak hainbat alditan errepikatuko diren:

  1. fib(5)
  2. fib(4) + fib(3)
  3. (fib(3) + fib(2)) + (fib(2) + fib(1))
  4. ((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
  5. (((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))

Ikus daitekenez, hainbat alditan deitu dugu fib programa, parametro berdinekin. Parametro handiagoekin, fib balio berdin gehiago birkalkulatuko lirateke.

Arazo hau konpontzeko, problema programazio dinamikoaren bidez burutu dezakegu, memorizazioa erabiliz (kalkulatuak izan diren balioak gorde, ondoren berriz erabiltzeko). Era honetan, taula batean gordeko ditugu ateratzen joan ditugun balioak, ondoren hauek behar direnean erabiltzeko. Eratutako taula dimentsio bakarrekoa izango litzateke, 0tik n-ra.

Hau kalkulatuko luken programa, Bottom-up erabiliz:

FUNC Fibonacci (↓n: ARRUNTA): ARRUNTA
ALDAGAIAK
   taula: ARRAY [0..n] ARRUNTAZKOA
   i: ARRUNTA
HASIERA
   BALDIN n = 0 ORDUAN
       ITZULI 0
   BESTELA, BALDIN n = 1 ORDUAN
       ITZULI 1
   BESTELA
       taula[0] := 0
       taula[1] := 1
       FOR i = 2TIK nRA EGIN
           taula[i] := taula[i-1] + taula[i-2]
       FOR AMAITU
       ITZULI taula[n]
   BALDIN AMAITU
AMAITU

Fibonacci funtzioak O(n)ko kostua izango du, 2^n ordez (zuhaitz bitar bat eratzen duelako memorian).  Beste adibide bat, Top-Down erabilita, kalkulatutako azken bi balioekin geratzea izango litzateke, erabilgarriak direnak hurrengo zenbakiak kalkulatzeko.

Honako egitura izango luke:

FUNC Fibonacci (↓n: ARRUNTA, ↨taula: ARRAY [0..n] ARRUNTAZKOA): ARRUNTA
    ALDAGAIAK
       i: ARRUNTA
    HASIERA
        BALDIN n <= 1 ORDUAN
            ITZULI n
       AMAITU BALDIN
        BALDIN taula[n-1] = -1 ORDUAN
           taula[n-1] := Fibonacci(n-1, taula)
        BALDIN AMAITU
        BALDIN taula[n-2] = -1 ORDUAN
           taula[n-2] := Fibonacci(n-2, taula)
        BALDIN AMAITU
           taula[n] := taula[n-1] + taula[n-2] 
       ITZULI taula[n]
AMAITU

Kanpo estekak