← Computer Programming I

Problem 1: Finding the Top Performer

Problem Statement: You are a teaching assistant and need to find the student with the highest score from a recent exam. You are given two lists: one with student names and another with their corresponding scores. The lists are “parallel,” meaning the student at index i in the student_names list earned the score at index i in the student_scores list.

Write a Python function called find_best_student(student_names, student_scores) that takes these two lists as arguments.

Requirements:

  1. The function should determine the highest score in the student_scores list.
  2. It should then find the name of the student who achieved that score.
  3. The function must return the name (a string) of the student with the highest score.

Constraints & Edge Cases:

  • If the input lists are empty, the function should return None.
  • In case of a tie for the highest score, the function should return the name of the first student who achieved that score.
  • You can assume both lists will always have the same number of elements.

Example Cases:

  • names = ["Alice", "Bob", "Charlie", "David"]
  • scores = [88, 92, 99, 95]
  • Calling find_best_student(names, scores) should return "Charlie".

  • names = ["Eve", "Frank", "Grace"]
  • scores = [95, 85, 95]
  • Calling find_best_student(names, scores) should return "Eve" (due to the tie-breaker rule).

  • names = []
  • scores = []
  • Calling find_best_student(names, scores) should return None.

Expected Output/Behavior:

Top student is: Charlie
Top student in a tie is: Eve
Result for empty lists: None

Problem 2: In-Place Data Cleaning

Problem Statement: In data analysis, it’s common to clean a dataset by removing invalid entries. You are tasked with writing a function that removes “outliers” from a list of measurements.

Write a Python function called remove_outliers(data, min_val, max_val) that modifies a list of numbers in-place.

Requirements:

  1. The function takes a list of numbers (data), a minimum valid value (min_val), and a maximum valid value (max_val).
  2. It should iterate through the data list and remove any number that is less than min_val or greater than max_val.
  3. The list must be modified directly. The function should not create a new list and should not return any value.

Important Hint: Be careful when removing elements from a list you are iterating over. Using a standard for item in data: loop can lead to skipping elements. Consider using a while loop with an index.

Example Cases:

  • measurements = [8, 5, 12, 1, 9, 20, 15]
  • Calling remove_outliers(measurements, 5, 15) should change the measurements list to be [8, 5, 12, 9, 15].

  • temps = [-5, 10, 0, 35, 15, 40, 20]
  • Calling remove_outliers(temps, 0, 30) should change the temps list to be [10, 0, 15, 20].

  • edge_case = [100, 1, 101, 2, 102]
  • Calling remove_outliers(edge_case, 0, 10) should change edge_case to be [1, 2]. This case is designed to fail if you use a simple for loop.

Expected Output/Behavior:

Original measurements: [8, 5, 12, 1, 9, 20, 15]
Cleaned measurements:  [8, 5, 12, 9, 15]
--------------------
Original temps: [-5, 10, 0, 35, 15, 40, 20]
Cleaned temps:  [10, 0, 15, 20]
--------------------
Original edge case: [100, 1, 101, 2, 102]
Cleaned edge case:  [1, 2]

Problem 3: Zigzag Merge

Problem Statement: Write a function called zigzag_merge(list1, list2) that takes two lists of items as input. The function should create and return a new list by merging the two input lists in a “zigzag” or alternating pattern.

Requirements:

  1. The new list should start with the first element of list1, followed by the first element of list2, then the second element of list1, the second element of list2, and so on.
  2. The function should be able to handle lists of unequal length. Once the shorter list runs out of elements, the function should append all the remaining elements from the longer list to the end of the merged list.
  3. The original input lists should not be modified.

Example Cases:

  • list_a = [1, 2, 3]
  • list_b = ['A', 'B', 'C']
  • Calling zigzag_merge(list_a, list_b) should return [1, 'A', 2, 'B', 3, 'C'].

  • list_c = [1, 2]
  • list_d = ['A', 'B', 'C', 'D']
  • Calling zigzag_merge(list_c, list_d) should return [1, 'A', 2, 'B', 'C', 'D'].

  • list_e = [1, 2, 3, 4, 5]
  • list_f = ['A']
  • Calling zigzag_merge(list_e, list_f) should return [1, 'A', 2, 3, 4, 5].

Expected Output/Behavior:

Merged equal lists: [1, 'A', 2, 'B', 3, 'C']
Merged with longer second list: [1, 'A', 2, 'B', 'C', 'D']
Merged with longer first list: [1, 'A', 2, 3, 4, 5]

Problem 4: Longest Consecutive Run

Problem Statement: Write a Python function called find_longest_run(data) that takes a single list of items (data) as an argument. The function should find the longest “run” of consecutive, identical elements and return the length of that run.

Requirements:

  1. A “run” is a sequence of one or more identical elements that appear next to each other.
  2. The function must return an integer representing the length of the longest run found in the list.
  3. If multiple runs have the same maximum length, the function can simply return that length.

Constraints & Edge Cases:

  • If the input list is empty, the longest run is 0.
  • If the list has no repeated consecutive elements (e.g., [1, 2, 3, 4]), the longest run is 1.
  • The function should correctly handle cases where the longest run occurs at the very beginning or the very end of the list.

Example Cases:

  • find_longest_run([1, 2, 2, 3, 3, 3, 2, 2]) should return 3 (because of the three consecutive 3s).
  • find_longest_run(['A', 'A', 'A', 'B', 'C', 'C']) should return 3 (for the three As).
  • find_longest_run([5, 5, 5, 5, 5]) should return 5.
  • find_longest_run([1, 2, 3, 4, 5]) should return 1.
  • find_longest_run([]) should return 0.

Expected Output/Behavior:

Longest run in [1, 2, 2, 3, 3, 3, 2, 2]: 3
Longest run in ['A', 'A', 'A', 'B', 'C', 'C']: 3
Longest run in [5, 5, 5, 5, 5]: 5
Longest run in [1, 2, 3, 4, 5]: 1
Longest run in []: 0

Problem 5: Data Smoothing (Moving Average)

Problem Statement: In data science, noisy data is often “smoothed” to reveal trends. A common technique is the Simple Moving Average.

Write a function called calculate_moving_average(data, window_size) that takes a list of numbers (data) and an integer (window_size).

Requirements:

  1. The function should calculate the average of every consecutive “window” of window_size elements in the list.
  2. For example, if window_size is 3, you first average elements at indices 0, 1, 2, then average elements at 1, 2, 3, then 2, 3, 4, and so on, until you reach the end of the list.
  3. Return a new list containing these calculated averages.

Constraints & Edge Cases:

  • If the window_size is greater than the total length of the data list, the function cannot calculate any full windows. It should return an empty list [].
  • If window_size is less than or equal to 0, it should also return an empty list [].

Example Trace: If data = [10, 20, 30, 40, 50] and window_size = 3:

  • Window 1: [10, 20, 30] -> Average is (10+20+30)/3 = 20.0
  • Window 2: [20, 30, 40] -> Average is (20+30+40)/3 = 30.0
  • Window 3: [30, 40, 50] -> Average is (30+40+50)/3 = 40.0
  • Result: [20.0, 30.0, 40.0]

Example Cases:

  • calculate_moving_average([1, 2, 3, 4, 5], 2) should return [1.5, 2.5, 3.5, 4.5].
  • calculate_moving_average([10, 20, 30, 40, 50], 3) should return [20.0, 30.0, 40.0].
  • calculate_moving_average([5, 10, 15], 5) should return [] (window too large).
  • calculate_moving_average([8, 12], 1) should return [8.0, 12.0] (window of size 1 is just the numbers themselves).

Expected Output/Behavior:

Moving average (window 3) of [10, 20, 30, 40, 50]: [20.0, 30.0, 40.0]
Moving average (window 2) of [1, 2, 3, 4, 5]: [1.5, 2.5, 3.5, 4.5]
Moving average (window 5 - too big) of [5, 10, 15]: []