In this section, we will provide practical exercises to help you apply the concepts of algorithm optimization. These exercises are designed to reinforce your understanding of code optimization, efficient memory usage, and algorithm parallelization. Each exercise includes a problem statement, a sample solution, and an explanation of the optimization techniques used.
Exercise 1: Optimizing a Sorting Algorithm
Problem Statement
You are given an array of integers. Your task is to sort the array in ascending order. However, you need to optimize the sorting algorithm to achieve better performance.
Sample Code
def bubble_sort(arr): n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr # Example usage arr = [64, 34, 25, 12, 22, 11, 90] sorted_arr = bubble_sort(arr) print("Sorted array:", sorted_arr)
Optimization Task
Optimize the above bubble sort algorithm to use a more efficient sorting algorithm.
Optimized Solution
def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) # Example usage arr = [64, 34, 25, 12, 22, 11, 90] sorted_arr = quick_sort(arr) print("Sorted array:", sorted_arr)
Explanation
- Bubble Sort has a time complexity of O(n^2), which is inefficient for large datasets.
- Quick Sort has an average time complexity of O(n log n), making it much more efficient for sorting large arrays.
Exercise 2: Reducing Space Complexity
Problem Statement
You are given a list of integers. Your task is to find the maximum sum of a contiguous subarray. Optimize the algorithm to use less memory.
Sample Code
def max_subarray_sum(arr): n = len(arr) max_sum = -float('inf') for i in range(n): for j in range(i, n): current_sum = sum(arr[i:j+1]) if current_sum > max_sum: max_sum = current_sum return max_sum # Example usage arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] max_sum = max_subarray_sum(arr) print("Maximum subarray sum:", max_sum)
Optimization Task
Optimize the above algorithm to reduce its space complexity.
Optimized Solution
def max_subarray_sum(arr): max_sum = arr[0] current_sum = arr[0] for i in range(1, len(arr)): current_sum = max(arr[i], current_sum + arr[i]) max_sum = max(max_sum, current_sum) return max_sum # Example usage arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] max_sum = max_subarray_sum(arr) print("Maximum subarray sum:", max_sum)
Explanation
- The initial algorithm uses O(n^2) space complexity due to the creation of subarrays.
- The optimized algorithm uses Kadane's Algorithm, which has a space complexity of O(1) and a time complexity of O(n).
Exercise 3: Parallelizing an Algorithm
Problem Statement
You are given a large list of integers. Your task is to compute the sum of all elements in the list. Optimize the algorithm by parallelizing the computation.
Sample Code
def compute_sum(arr): total_sum = 0 for num in arr: total_sum += num return total_sum # Example usage arr = [i for i in range(1000000)] total_sum = compute_sum(arr) print("Total sum:", total_sum)
Optimization Task
Optimize the above algorithm by parallelizing the computation.
Optimized Solution
from multiprocessing import Pool def partial_sum(arr): return sum(arr) def compute_sum(arr): num_workers = 4 chunk_size = len(arr) // num_workers chunks = [arr[i:i + chunk_size] for i in range(0, len(arr), chunk_size)] with Pool(num_workers) as pool: results = pool.map(partial_sum, chunks) return sum(results) # Example usage arr = [i for i in range(1000000)] total_sum = compute_sum(arr) print("Total sum:", total_sum)
Explanation
- The initial algorithm computes the sum sequentially, which can be slow for large datasets.
- The optimized algorithm uses the
multiprocessing
module to parallelize the computation, dividing the array into chunks and summing each chunk in parallel. This reduces the overall computation time.
Conclusion
In this section, we have covered exercises that focus on optimizing algorithms for better performance. We explored techniques such as using more efficient algorithms, reducing space complexity, and parallelizing computations. By practicing these exercises, you should have a better understanding of how to optimize algorithms in various scenarios.
Next, you can proceed to the final projects to apply all the concepts learned throughout the course.