Burak IŞIKLI

Computer scientist living in Istanbul, Turkey. Data scientist at Turkcell, Traveller, Rower, Sailor

Twitter Github Linkedin Mail

Dinamik Programlama (Dynamic Programming)

Dinamik programlama bir problemi çözerken aynı alt-problemi birden fazla çözmemiz gereken durumlarda bu alt-problemi birden fazla kez çözmemizi engelleyen bir tekniktir. Dinamik programalama, matematik ve bilgisayar bilimlerinde karmaşık problemleri çözmek için kullanılan bir metottur. Overlapping subproblems ve optimal substructure denilen problemlerde uygunabiliyor.

Eğer problemimiz kendi içinde alt problemlere ayrılabiliyorsa overlapping subproblems’dir. Buna en iyi örnek Fibanicci serisidir. Daha önceden bu yazımda fibonacci serisini anlatmıştım.

Shortest Path Optimal
Substructure

Fakat problemimiz kendi içinde alt problemlere ayrıldığında daha iyi bir performans sağlıyorsa buna optimal substructure deniliyor. Buna en iyi örnek ise greedy algoritmasıdır. Dinamik programlamada geçen programlama sözcüğü sadece bilgisayar programlarını kapsamamaktadır, çünkü burada kullandığımız program sözcüğü “matematiksel programlama” dan gelmektedir, ki bu da kısaca sıkça kullandığımız optimizasyon(bir nevi iyileştirme demek optimizasyon belki kullanmayan da vardır :) kelimesine denk gelmektedir.

Dinamik programlama uygulamalarımızda temel olarak 3 teknikten faydalanacağız:

  • Çözümü aynı olan alt-problemler
  • Büyük bir problemi küçük parçalara bölmek ve bu küçük parçaları kullanarak baştaki büyük problemimizin sonucuna ulaşmak
  • Çözdüğümüz her alt-problemin sonucunu bir yere not almak ve gerektiğinde bu sonucu kullanarak aynı problemi tekrar tekrar çözmeyi engellemek.

Şimdi örneğimiz olan fibonacci dizimize başlayalım. Fibonacci dizimizi kısaca bir hatırlayalım.

Eğer herhangi bir sayının Fibonacci karşılığına F(n) dersek,

F(0) = F(1) = 1 olmak üzere

F(n) = F(n-1) + F(n-2)’dir.

İlk yaptığımız prosesimizin fonksiyonunu hatırlayalım:

fib.cpp

// Recursive Function  
int fib(int num) {  
if ((num == 0) || (num == 1))  
return num;  
else  
return (fib(num - 1) + fib(num - 2));  
}

Bu kadar kısa bir kod yazarak çözümü sağlamıştık. Ancak bu yöntem kodun kısa olmasına rağmen performans açısından kötü bir yöntemdir.  Nedenine gelirsek mesela F(3)’ü adım adım hesaplayalım:

F(4) = (F(2) + F(1)))

= ((F(1) + F(0)) + F(1)) + F(2)

= ((F(1) + F(0)) + F(1)) + (F(1) + F(0))

Görüldüğü gibi F(4) değerini hesaplarken F(1)’ye 2 defa ihtiyacımız oldu ama 2 seferde de hesapladık. Prosesimizi değiştirerek bir de şu hale getirelim.

fib2.cpp

/*
 *
 * Program:  Fibonacci Series v2
 * Programmer: Burak ISIKLI
 *
 */

#include <iostream.h>

unsigned int fib2(unsigned int num);

unsigned int fibArray[1000];

// Main Function
int main() {
 unsigned int number, result;
 cout << "Fibonacci serisinin(fib(x)) sayisini giriniz x = ";
 cin >> number;

 result = fib2(number);
 cout << "fib( " << number << " ) = " << result << "\n\n";

 return 0;
}

unsigned int fib2(unsigned int num)
{
 unsigned int i = 2;
 fibArray[0] = fibArray[1] = 1;
 while(i <= num)
 {
 fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
 i++;
 }
 return fibArray[num];
}

Burada yaptığımız işlem ise şu. Hesapladığımız Fibonacci sayılarını bir diziye kaydediyoruz. Daha sonra lazım olunca bu değerleri direkt diziden hazır bi şekilde alıyoruz. Böylece aynı işlemi tekrar tekrar yapmaktan kurtuluyoruz.

Performans karşılaştırması yapacak olursak kendi bilgisayarımda fibonacci(50) 8dk. 36 sn’de sonuç verirken fibonacci2(50) 0.003 sn’de sonuç veriyor. Daha bilimsel konuşacak olursak problemi temelde aynı mantıkla çalışan iki fonksiyonun birincisi O(2\^n) iken ikincisi O(n) zamanda çözmektedir.

Kaynaklar:

http://en.wikipedia.org/wiki/Dynamic_programming

http://en.wikipedia.org/wiki/Optimal_substructure

http://e-bergi.com/2008/Mart/Dinamik-Programlama

[sourcecode language=”cpp”] /* * * Program:  Fibonacci Series v2 * Programmer: Burak ISIKLI * */ #include unsigned int fib2(unsigned int num); unsigned int fibArray[1000]; // Main Function int main() { unsigned int number, result; cout << "Fibonacci serisinin(fib(x)) sayisini giriniz x = "; cin >> number; result = fib2(number); cout << "fib( " << number << " ) = " << result << "\n\n"; return 0; } unsigned int fib2(unsigned int num) { unsigned int i = 2; fibArray[0] = fibArray[1] = 1; while(i <= num) { fibArray[i] = fibArray[i - 1] + fibArray[i - 2]; i++; } return fibArray[num]; } [/sourcecode]

Comments !