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.
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
/* * * 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]; }Reklamlar
Fibonacci Serisi
Recursive(Rekürsif) fonksiyonlar öğretilirken en çok kullanılan örnek fibonacci serisidir. Bu seri özeldir. Serinin ilk iki değeri 0 ve 1’dir. Sonrasında ise hesaplanacak sayının bir ve iki eksiğinin fonksiyonlarındaki değerlerinde toplanmasıyla oluşur. Başarılı bir fibonacci serisi 1.618….’a yakınsar. Buna altın oran(golden ratio) veya altın ortalama(golden mean) denir. 0, 1, 1, 2, 3, 5, 8, 13, 21, … şeklinde devam eden serinin matematiksel fonksiyonu şu şekildedir:
fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)
Bu fonksiyonun kodu:
// Program: Fibonacci Series
// Programmer: Burak ISIKLI
#include
// Prototype Function
int fib(int num);
// Main Function
int main() {
int number, result;
cout << "Fibonacci serisinin(fib(x)) sayisini giriniz x = ";
cin >> number;
result = fib(number);
cout << "fib( " << number << " ) = " << result << "\n\n";
return 0;
}
// Recursive Function
int fib(int num) {
if ((num == 0) || (num == 1))
return num;
else
return (fib(num - 1) + fib(num - 2));
}
[/sourcecode]
Bu programımızda kullanıcıya hesaplatmak istediği fibonacci değerini soruyoruz. Hesaplatarak ekrana yazdırıyoruz.
leave a comment