Problem Challenge 2
Dry-run templates
This is the implementation of the solution provided for the problem posed in the Problem Challenge 2 lesson:
from heapq import *class job:def __init__(self, start, end, cpu_load):self.start = startself.end = endself.cpu_load = cpu_loaddef __lt__(self, other):# min heap based on job.endreturn self.end < other.enddef find_max_cpu_load(jobs):# sort the jobs by start timejobs.sort(key=lambda x: x.start)max_cpu_load, current_cpu_load = 0, 0min_heap = []for j in jobs:# remove all the jobs that have endedwhile(len(min_heap) > 0 and j.start >= min_heap[0].end):current_cpu_load -= min_heap[0].cpu_loadheappop(min_heap)# add the current job into min_heapheappush(min_heap, j)current_cpu_load += j.cpu_loadmax_cpu_load = max(max_cpu_load, current_cpu_load)return max_cpu_loaddef main():print("Maximum CPU load at any time: " + str(find_max_cpu_load([job(1, 4, 3), job(2, 5, 4), job(7, 9, 6)])))print("Maximum CPU load at any time: " + str(find_max_cpu_load([job(6, 7, 10), job(2, 4, 11), job(8, 12, 15)])))print("Maximum CPU load at any time: " + str(find_max_cpu_load([job(1, 4, 2), job(2, 4, 1), job(3, 6, 5)])))main()
Students may be encouraged to run through the provided solution with the following sample inputs:
Sample Input #1
jobs: [job(1, 4, 3), job(2, 5, 4), job(7, 9, 6)]
Iteration No. | Line No. | min_heap | current_cpu_load | max_cpu_load |
- | 18-19 | [] | 0 | 0 |
1 | 27-29 | [(1,4,3)] | 3 | 3 |
2 | 27-29 | [(1,4,3),(2,5,4)] | 7 | 7 |
3 | 23-25 | [] | 0 | 0 |
3 | 27-29 | [(7,9,6)] | 6 | 7 |
Sample Input #2
jobs: [job(6, 7, 10), job(2, 4, 11), job(8, 12, 15)]
Iteration No. | Line No. | min_heap | current_cpu_load | max_cpu_load |
- | 18-19 | [] | 0 | 0 |
1 | 27-29 | [(2,4,11)] | 11 | 11 |
2 | 23-25 | [] | 0 | 11 |
2 | 27-29 | [(6,7,10)] | 10 | 11 |
3 | 23-25 | [] | 0 | 11 |
3 | 27-29 | [(8,12,15)] | 15 | 15 |
Step-by-step solution construction
Let’s first write code to find the sum of workloads of all the jobs without considering the intervals (lines 34-43). Also, we will be pushing each job into a heap to be used in the next steps.
Notice the function print_arr
used to print the trace log for a better understanding of the program execution. print_arr
is a global function that prints an array of objects of type job
. For each object, __str__
is called by default to convert the object to string
. We use this fact and override __str__
for class job
to return its string representation (lines 14-15).
from heapq import *class job:def __init__(self, start, end, cpu_load):self.start = startself.end = endself.cpu_load = cpu_loaddef __lt__(self, other):# min heap based on job.endreturn self.end < other.enddef __str__(self):return "("+ str(self.start)+", "+ str(self.end)+ ", " + str(self.cpu_load) + ")"def print_arr(min_heap):print("[", end="")index = 0for j in min_heap:if(index > 0):print(",",end="")print(j,end="")index += 1print("]")def find_max_cpu_load(jobs):# sort the jobs by start timejobs.sort(key=lambda x: x.start)max_cpu_load, current_cpu_load = 0, 0min_heap = []index = 0for j in jobs:print("\tIteration: "+str(index))index +=1# add the current job into min_heapheappush(min_heap, j)print("\t\tmin_heap after adding current job to the min_heap: ",end = "")print_arr(min_heap)current_cpu_load += j.cpu_loadprint("\t\tcurrent_cpu_load: " + str(current_cpu_load))max_cpu_load = max(max_cpu_load, current_cpu_load)print("\t\tmax_cpu_load: " + str(max_cpu_load))return max_cpu_loaddef main():jobs = [[job(1, 4, 3), job(2, 5, 4), job(7, 9, 6)],[job(6, 7, 10), job(2, 4, 11), job(8, 12, 15)],[job(1, 4, 2), job(2, 4, 1), job(3, 6, 5)]]for j in jobs:print("Finding maximum load for: ", end="")print_arr(j)result = find_max_cpu_load(j)print("Maximum CPU load at any time: " + str(result))print(("-"*100)+"\n")main()
Let’s refine the code to take the intervals into consideration.
Before a new job
to the min_heap
, we remove all the jobs that have ended before the current job. Since we compute current_cpu_load
for the jobs present in the min_heap, when we pop the jobs from the min_heap
, we also exclude current_work_load for the jobs that have ended (lines 41-43).
We will keep adding the overlapped jobs to the min_heap
until a non-overlapping job is found (line 47). We will also compute the sum of the workloads of these jobs (line 50).
We also maintain another variable max_cpu_load
to track the maximum CPU load of the overlapping jobs at any time. When the iteration of all the jobs
is complete, the value in max_cpu_load
represents the maximum load attained during the job executions.
from heapq import *class job:def __init__(self, start, end, cpu_load):self.start = startself.end = endself.cpu_load = cpu_loaddef __lt__(self, other):# min heap based on job.endreturn self.end < other.enddef __str__(self):return "("+ str(self.start)+", "+ str(self.end)+ ", " + str(self.cpu_load) + ")"def print_arr(min_heap):print("[", end="")index = 0for j in min_heap:if(index > 0):print(",",end="")print(j,end="")index += 1print("]")def find_max_cpu_load(jobs):# sort the jobs by start timejobs.sort(key=lambda x: x.start)max_cpu_load, current_cpu_load = 0, 0min_heap = []index = 0for j in jobs:print("\tIteration: "+str(index))print("\t\tmin_heap: ",end="")print_arr(min_heap)# remove all the jobs that have endedprint("\t\tremoving all the jobs that have ended before the current job: " + str(j))while(len(min_heap) > 0 and j.start >= min_heap[0].end):current_cpu_load -= min_heap[0].cpu_loadheappop(min_heap)print("\t\tmin_heap after removing all earlier jobs: ", end= "")print_arr(min_heap)# add the current job into min_heapheappush(min_heap, j)print("\t\tmin_heap after adding current job to the min_heap: ",end = "")print_arr(min_heap)current_cpu_load += j.cpu_loadprint("\t\tcurrent_cpu_load: " + str(current_cpu_load))max_cpu_load = max(max_cpu_load, current_cpu_load)print("\t\tmax_cpu_load: " + str(max_cpu_load))index +=1return max_cpu_loaddef main():jobs = [[job(1, 4, 3), job(2, 5, 4), job(7, 9, 6)],[job(6, 7, 10), job(2, 4, 11), job(8, 12, 15)],[job(1, 4, 2), job(2, 4, 1), job(3, 6, 5)]]for j in jobs:print("Finding maximum load for: ", end="")print_arr(j)result = find_max_cpu_load(j)print("Maximum CPU load at any time: " + str(result))print(("-"*100)+"\n")main()