𝔩𝔢𝔩𝕠𝔭𝔢𝔷
Connect With Me on LinkedIn

Python Study

Optimal Freelancing

Maximizing Freelance Job Profits in a 7-Day Window

Introduction

Optimizing job selection to maximize profit within a constrained timeframe is a common problem in resource allocation and scheduling. This article explores an algorithm to determine the maximum profit from a list of freelance jobs with varying deadlines within a 7-day window. We'll delve into the implementation details, optimization strategies, and the reasoning behind our approach.

Problem Statement

Given:

  • A list of jobs, each with a deadline and a payment
  • Each job takes one full day to complete
  • A 7-day window for job completion

Objective:

  • Determine the maximum profit that can be achieved in the 7-day window

Example:

  • Input:
    jobs = [
        {"deadline": 1, "payment": 1},
        {"deadline": 2, "payment": 1},
        {"deadline": 2, "payment": 2},
    ]
    
  • Output: 3 (Job 0 and Job 2 are completed, Job 1 is not)

Assumption:

  • Jobs can be completed on or before their deadline

Strategy and Hypothesis

We should prioritize jobs with later deadlines and higher payments to maximize profit. A greedy approach, starting from the last day and working backward, allows us to:

  1. Schedule jobs with extended deadlines first
  2. Resolve conflicts by choosing the most lucrative jobs
  3. Ensure earlier deadlines are met if possible

For example, given jobs A(deadline: 1, payment: 1), B(deadline: 3, payment: 2), C(deadline: 3, payment: 3):

  • Day 3: Schedule job C (highest payment with the latest deadline)
  • Day 2: Schedule job B (next highest payment with latest available deadline)
  • Day 1: Schedule job A (meets its deadline)

Implementation

Initial Approach

def optimalFreelancing(jobs):
    expectedPayout = 0

    for deadline in range(7, 0, -1):
        candidate = None
        candidatePay = 0
        for job in jobs:
            if job["deadline"] < deadline:
                continue
            if job.get("scheduled", False):
                continue
            if job["payment"] > candidatePay:
                candidate = job
                candidatePay = job["payment"]
        if candidate is not None:
            candidate["scheduled"] = True
            expectedPayout += candidatePay

    return expectedPayout

This implementation iterates through each day, selecting the highest-paying unscheduled job that meets the current deadline.

Improved Approach

We can optimize the initial approach by sorting the jobs by payment in descending order, reducing the number of iterations:

def optimalFreelancing(jobs):
    jobs.sort(key=lambda x: x["payment"], reverse=True)
    schedule = [False] * 7
    total_payment = 0

    for job in jobs:
        latest_day = min(job["deadline"], 7) - 1
        for day in range(latest_day, -1, -1):
            if not schedule[day]:
                schedule[day] = True
                total_payment += job["payment"]
                break

    return total_payment

This version sorts jobs once and then attempts to schedule each job on the latest possible day.

Optimization Analysis

Space Complexity

  • Initial Approach: O(n) due to modifying the input jobs list
  • Improved Approach: O(1) as it uses a fixed-size schedule array

Time Complexity

  • Initial Approach: O(n * 7) = O(n), where n is the number of jobs
  • Improved Approach: O(n log n) due to sorting, but with faster practical performance for scheduling

Optimal Space & Time

The improved approach offers a good balance of time and space efficiency:

  • Sorting jobs by payment reduces the number of iterations needed for scheduling
  • Using a fixed-size schedule array keeps space complexity constant
  • The algorithm remains efficient even with a large number of jobs

Key Takeaways

  1. Greedy algorithms can effectively schedule problems with clear priorities (e.g., maximizing payment).
  2. Sorting input data can simplify subsequent processing steps.
  3. Backward iteration can be useful in deadline-based scheduling problems.
  4. Optimizing for both time and space often involves trade-offs and careful algorithm design.
  5. Consider edge cases, such as jobs with identical deadlines or payments, when designing and testing solutions.

Conclusion

The optimal freelancing problem demonstrates the application of greedy algorithms in scheduling and profit maximization scenarios. By prioritizing high-paying jobs and working backward from deadlines, we've developed an efficient solution that maximizes profit within given constraints. This approach solves the immediate problem and illustrates broader algorithm design principles, including the importance of sorting for optimization and the balance between time and space complexity. As we tackle more complex scheduling and optimization problems, the insights gained from this exercise—particularly in greedy algorithms and efficient data handling—will prove invaluable in developing robust and efficient solutions.

Previous
Removing Duplicates