Forth.
md 2024-10-27
Assignment 4
Question 1
import time
fib_memo = {}
recursive_calls = 0
dictionary_lookups = 0
dictionary_updates = 0
def fibonacci(n):
global recursive_calls, dictionary_lookups, dictionary_updates
recursive_calls += 1
if n in fib_memo:
dictionary_lookups += 1
return fib_memo[n]
if n <= 1:
return n
result = fibonacci(n - 1) + fibonacci(n - 2)
fib_memo[n] = result
dictionary_updates += 1
return result
. Memoization Dictionary (fib_memo): Stores previously computed Fibonacci values to avoid
redundant calculations. This speeds up the function by preventing recalculations of the same values.
. Tracking Variables:
• recursive_calls: Counts how many times the fibonacci function is called.
• dictionary_lookups: Counts the number of times the program retrieves a value from fib_memo.
• dictionary_updates: Counts the number of new entries added to fib_memo.
. fibonacci Function:
• Increments recursive_calls each time the function is called.
• Checks if n is already in fib_memo. If it is, increments dictionary_lookups and returns the cached
value.
• If n is 0 or 1, returns n as the base case.
• Otherwise, calculates fibonacci(n - 1) + fibonacci(n - 2) recursively.
• Stores the result in fib_memo and increments dictionary_updates.
• Returns the computed Fibonacci number.
By storing intermediate results, this function avoids the exponential time complexity typical of simple
recursion, making it much faster for large n.
1/4
Forth.md 2024-10-27
Example Usage:
n = 10
start_time = time.time()
result = fibonacci(n)
execution_time = time.time() - start_time
# Output results
print(f"Fibonacci({n}) = {result}")
print(f"Recursive calls: {recursive_calls}")
print(f"Dictionary lookups: {dictionary_lookups}")
print(f"Dictionary updates: {dictionary_updates}")
print(f"Execution time: {execution_time} seconds")
Output:
Fibonacci(10) = 55
Recursive calls: 19
Dictionary lookups: 7
Dictionary updates: 9
Execution time: 7.867813110351562e-06 seconds
Question 2
import matplotlib.pyplot as plt
import time
def fibonacci_recursive(n):
if n <= 1:
return n
return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)
def measure_performance(n):
global recursive_calls, dictionary_lookups, dictionary_updates
recursive_calls = dictionary_lookups = dictionary_updates = 0
start_time = time.time()
fibonacci(n)
dp_time = time.time() - start_time
dp_calls = recursive_calls
start_time = time.time()
fibonacci_recursive(n)
recursive_time = time.time() - start_time
simple_calls = 2**n if n < 32 else "Too many calls"
return dp_time, recursive_time, dp_calls, simple_calls
2/4
Forth.md 2024-10-27
n_values = range(1, 33)
dp_times, recursive_times, dp_calls, simple_calls = [], [], [], []
for n in n_values:
dp_time, recursive_time, dp_call, simple_call = measure_performance(n)
dp_times.append(dp_time)
recursive_times.append(recursive_time)
dp_calls.append(dp_call)
simple_calls.append(simple_call)
plt.figure(figsize=(5, 12))
plt.subplot(2, 1, 1)
plt.plot(n_values, dp_times, label='Dynamic Programming')
plt.plot(n_values, recursive_times, label='Simple Recursion')
plt.xlabel("Fibonacci Number (n)")
plt.ylabel("Time (seconds)")
plt.legend()
plt.title("Time Comparison for Fibonacci Calculation")
plt.subplot(2, 1, 2)
plt.plot(n_values, dp_calls, label='Dynamic Programming')
plt.plot(n_values, [2**n for n in n_values], label='Simple Recursion')
plt.xlabel("Fibonacci Number (n)")
plt.ylabel("Number of Calls")
plt.yscale("log")
plt.legend()
plt.title("Function Call Comparison for Fibonacci Calculation")
plt.tight_layout()
plt.show()
This code uses the previously defined fibonacci function. It compares the performance of calculating
Fibonacci numbers using dynamic programming with memoization and simple recursion.
. fibonacci_recursive(n): Calculates the Fibonacci number using simple recursion.
. measure_performance(n):
• Calculates the execution time and function calls for both methods.
• For dynamic programming (fibonacci function), it tracks the time and counts recursive calls.
• For simple recursion, it measures time and estimates the number of calls (approximated as 2^n).
. Data Collection:
• Runs measure_performance for Fibonacci numbers from 1 to 32, storing the time and call counts for
both methods.
. Plotting:
• The first plot compares the time taken by both methods.
• The second plot compares the number of function calls, using a logarithmic scale for clarity.
This visualization helps demonstrate that dynamic programming is significantly faster and involves far fewer
recursive calls than simple recursion as n increases.
3/4
Forth.md 2024-10-27
4/4