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.

© Copyright 2024. All rights reserved