← Back to DSA Course | Chapter 14: Dynamic Programming | Lesson 3 of 12

DP के साथ Fibonacci

DP के साथ Fibonacci हर बार scratch से recalculate करने के बजाय आखिरी दो numbers याद रखने जैसा है, इसलिए यह एक crazy लंबी job से एक छोटी job बन जाती है।
Syntax
markup
def fib(n):
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

Fibonacci DP

Plain recursive Fibonacci वही छोटी Fibonacci values बार-बार recompute करता है, इसलिए इसका running time exponentially बढ़ता है; हर value को पहली बार compute होने पर store करना उसी recursion को एक fast, DP-powered version में बदल देता है।

उदाहरण: Fibonacci DP

#include <iostream>
using namespace std;
int main() {
	int dp[11]; dp[0]=0; dp[1]=1;
	for (int i = 2; i <= 10; i++) dp[i] = dp[i-1] + dp[i-2];
	cout << "Stored values turn exponential recursion into fib(10) = " << dp[10] << " in linear time";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] dp = new int[11]; dp[0]=0; dp[1]=1;
		for (int i = 2; i <= 10; i++) dp[i] = dp[i-1] + dp[i-2];
		System.out.println("Stored values turn exponential recursion into fib(10) = " + dp[10] + " in linear time");
	}
}
dp = [0]*11
dp[1] = 1
for i in range(2, 11):
    dp[i] = dp[i-1] + dp[i-2]
print(f"Stored values turn exponential recursion into fib(10) = {dp[10]} in linear time")
#include <stdio.h>
int main() {
	int dp[11]; dp[0]=0; dp[1]=1;
	for (int i = 2; i <= 10; i++) dp[i] = dp[i-1] + dp[i-2];
	printf("Stored values turn exponential recursion into fib(10) = %d in linear time", dp[10]);
	return 0;
}

Space Optimization

चूंकि fib(n) compute करने के लिए सिर्फ इससे ठीक पहले वाली दो values चाहिए, आपको पूरी table store करने की ज़रूरत नहीं — बस दो running variables रखना और हर step उन्हें update करना memory को O(n) से O(1) तक घटाता है।

उदाहरण: Space Optimization

#include <iostream>
using namespace std;
int main() {
	int prev2 = 0, prev1 = 1;
	for (int i = 2; i <= 10; i++) { int cur = prev1 + prev2; prev2 = prev1; prev1 = cur; }
	cout << "Only 2 variables needed, fib(10) = " << prev1;
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int prev2 = 0, prev1 = 1;
		for (int i = 2; i <= 10; i++) { int cur = prev1 + prev2; prev2 = prev1; prev1 = cur; }
		System.out.println("Only 2 variables needed, fib(10) = " + prev1);
	}
}
prev2, prev1 = 0, 1
for i in range(2, 11):
    prev2, prev1 = prev1, prev1 + prev2
print("Only 2 variables needed, fib(10) =", prev1)
#include <stdio.h>
int main() {
	int prev2 = 0, prev1 = 1;
	for (int i = 2; i <= 10; i++) { int cur = prev1 + prev2; prev2 = prev1; prev1 = cur; }
	printf("Only 2 variables needed, fib(10) = %d", prev1);
	return 0;
}

Memoization

एक memoized version natural top-down recursive structure रखता है (fib(n), fib(n-1) और fib(n-2) को call करता है) लेकिन पहले एक cache जांचता है, इसलिए n की हर distinct value सिर्फ एक बार compute होती है चाहे यह कितनी भी बार request की जाए।

उदाहरण: Memoization

#include <iostream>
#include <unordered_map>
using namespace std;
unordered_map<int,int> cache;
int fib(int n) {
	if (n <= 1) return n;
	if (cache.count(n)) return cache[n];
	return cache[n] = fib(n-1) + fib(n-2);
}
int main() { cout << "Recursive shape kept, fib(10) = " << fib(10); return 0; }
import java.util.*;
public class Main {
	static Map<Integer,Integer> cache = new HashMap<>();
	static int fib(int n) {
		if (n <= 1) return n;
		if (cache.containsKey(n)) return cache.get(n);
		int r = fib(n-1) + fib(n-2);
		cache.put(n, r);
		return r;
	}
	public static void main(String[] args) { System.out.println("Recursive shape kept, fib(10) = " + fib(10)); }
}
cache = {}
def fib(n):
    if n <= 1:
        return n
    if n in cache:
        return cache[n]
    cache[n] = fib(n-1) + fib(n-2)
    return cache[n]
print("Recursive shape kept, fib(10) =", fib(10))
#include <stdio.h>
int cache[11]={0}, computed[11]={0};
int fib(int n) {
	if (n <= 1) return n;
	if (computed[n]) return cache[n];
	computed[n] = 1;
	return cache[n] = fib(n-1) + fib(n-2);
}
int main() { printf("Recursive shape kept, fib(10) = %d", fib(10)); return 0; }

Complexity

Results store करना naive recursion के exponential time को linear time में बदल देता है, क्योंकि n distinct subproblems में से हर एक अब हर branching recursive call में recompute होने के बजाय बिल्कुल एक बार solve होता है।

उदाहरण: Complexity

#include <iostream>
using namespace std;
int main() {
	cout << "Naive recursion: O(2^n); DP (memo or tabulation): O(n), each subproblem solved once";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("Naive recursion: O(2^n); DP (memo or tabulation): O(n), each subproblem solved once");
	}
}
print("Naive recursion: O(2^n); DP (memo or tabulation): O(n), each subproblem solved once")
#include <stdio.h>
int main() {
	printf("Naive recursion: O(2^n); DP (memo or tabulation): O(n), each subproblem solved once");
	return 0;
}

Practice

Fibonacci आमतौर पर पहला DP example सिखाया जाता है क्योंकि इसके overlapping subproblems recursion tree में सीधे देखना आसान है, इसे harder problems से निपटने से पहले यह समझने का एक clean तरीका बनाते हुए कि memoization और tabulation क्यों मदद करते हैं।

उदाहरण: Practice

#include <iostream>
using namespace std;
int main() {
	cout << "Fibonacci's recursion tree makes overlapping subproblems easy to see -- the classic first DP example";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("Fibonacci's recursion tree makes overlapping subproblems easy to see -- the classic first DP example");
	}
}
print("Fibonacci's recursion tree makes overlapping subproblems easy to see -- the classic first DP example")
#include <stdio.h>
int main() {
	printf("Fibonacci's recursion tree makes overlapping subproblems easy to see -- the classic first DP example");
	return 0;
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
आम गलतियां
  1. बिना किसी cache के plain recursive fib(n - 1) + fib(n - 2) लिखना, जो exponential है।
  2. Base cases गलत पाना, जैसे dp[1] = 0।
  3. fib(n) के लिए हर value store करना जब सिर्फ पिछली दो चाहिए, memory waste करते हुए।

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.