From d40d30edbe5dbb02950909e0b0ad05097dfb7216 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 20:01:23 +0000 Subject: [PATCH 01/18] create a dir & README.md file for the new "Implement Laptop Allocation" task --- implement-laptop-allocation/README.md | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 implement-laptop-allocation/README.md diff --git a/implement-laptop-allocation/README.md b/implement-laptop-allocation/README.md new file mode 100644 index 00000000..ce85692d --- /dev/null +++ b/implement-laptop-allocation/README.md @@ -0,0 +1,42 @@ +In the prep, there was an exercise around finding possible laptops for a group of people. + +Your exercise is to extend this to actually allocate laptops to the people. + +Given these class definitions: + +from dataclasses import dataclass +from enum import Enum +from typing import List + +class OperatingSystem(Enum): + MACOS = "macOS" + ARCH = "Arch Linux" + UBUNTU = "Ubuntu" + +@dataclass(frozen=True) +class Person: + name: str + age: int + # Sorted in order of preference, most preferred is first. + preferred_operating_system: List[OperatingSystem] + + +@dataclass(frozen=True) +class Laptop: + id: int + manufacturer: str + model: str + screen_size_in_inches: float + operating_system: OperatingSystem +Write a function with this signature: + +def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: +Every person should be allocated exactly one laptop. + +If we define "sadness" as the number of places down in someone's ranking the operating system the ended up with (i.e. if your preferences were [UBUNTU, ARCH, MACOS] and you were allocated a MACOS machine your sadness would be 2), we want to minimise the total sadness of all people. If we allocate someone a laptop with an operating system not in their preferred list, treat them as having a sadness of 100. + +Maximum time in hours +3 + +How to submit +Submit a PR to this repo containing your function (and any supporting code). From 5856dd026b493563863e8757cde524fb9118ca21 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 20:06:07 +0000 Subject: [PATCH 02/18] create a .py file too hold the script --- implement-laptop-allocation/main.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 implement-laptop-allocation/main.py diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py new file mode 100644 index 00000000..e69de29b From a7f4634789e9bf244f3c33faf91aed891f367db0 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 20:08:14 +0000 Subject: [PATCH 03/18] add given code --- implement-laptop-allocation/main.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index e69de29b..713bcd7a 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -0,0 +1,24 @@ +from dataclasses import dataclass +from enum import Enum +from typing import List + +class OperatingSystem(Enum): + MACOS = "macOS" + ARCH = "Arch Linux" + UBUNTU = "Ubuntu" + +@dataclass(frozen=True) +class Person: + name: str + age: int + # Sorted in order of preference, most preferred is first. + preferred_operating_system: List[OperatingSystem] + + +@dataclass(frozen=True) +class Laptop: + id: int + manufacturer: str + model: str + screen_size_in_inches: float + operating_system: OperatingSystem \ No newline at end of file From 3b84ade1ca25380ba0504f369425f17cd97fc039 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 20:11:31 +0000 Subject: [PATCH 04/18] function to allocate laptop for each person --- implement-laptop-allocation/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 713bcd7a..715fadb4 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -21,4 +21,8 @@ class Laptop: manufacturer: str model: str screen_size_in_inches: float - operating_system: OperatingSystem \ No newline at end of file + operating_system: OperatingSystem + + + +def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: \ No newline at end of file From 8366a7aaf9b25960e65e26c2472a47fb7d65a771 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 20:45:49 +0000 Subject: [PATCH 05/18] importing from itertools module --- implement-laptop-allocation/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 715fadb4..7cc3ccef 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -1,6 +1,8 @@ from dataclasses import dataclass from enum import Enum from typing import List +from itertools import permutations + class OperatingSystem(Enum): MACOS = "macOS" From 78ff572cdecdaa9effedbfce6fbb23b57fcf1e60 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 20:58:11 +0000 Subject: [PATCH 06/18] create a helper function to handle the sadness level logic --- implement-laptop-allocation/main.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 7cc3ccef..0d66a285 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -26,5 +26,16 @@ class Laptop: operating_system: OperatingSystem +def sadness(person: Person, laptop: Laptop): + try: + return person.preferred_operating_system.index(laptop.operating_system) + except ValueError: + return 100 + + +if laptop.operating_system == person.preferred_operating_system: + return index(Person.preferred_operating_system) +else: + return "Sadness level is 100" def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: \ No newline at end of file From 18106deffbbb21c6572a763782830f28ff80fc63 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 20:59:06 +0000 Subject: [PATCH 07/18] remove helper function psudo code --- implement-laptop-allocation/main.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 0d66a285..73d84417 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -33,9 +33,4 @@ def sadness(person: Person, laptop: Laptop): return 100 -if laptop.operating_system == person.preferred_operating_system: - return index(Person.preferred_operating_system) -else: - return "Sadness level is 100" - def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: \ No newline at end of file From 2405f09addbfb27c705215ae3c4bb3b7213321c4 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 21:02:09 +0000 Subject: [PATCH 08/18] add a return type to helper function --- implement-laptop-allocation/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 73d84417..ab8fe039 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -26,7 +26,7 @@ class Laptop: operating_system: OperatingSystem -def sadness(person: Person, laptop: Laptop): +def sadness(person: Person, laptop: Laptop) ->int: try: return person.preferred_operating_system.index(laptop.operating_system) except ValueError: From fc69d05073ada47fcf82e6e238fdbab66593924a Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 22:45:57 +0000 Subject: [PATCH 09/18] expoloring all scenarios to find the least possible sadness level option --- implement-laptop-allocation/main.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index ab8fe039..2eca7c95 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -33,4 +33,11 @@ def sadness(person: Person, laptop: Laptop) ->int: return 100 -def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: \ No newline at end of file +def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: + + best_scenario_assignment = None + best_scenario_sadness = float("inf") + + for scenario in scenarios(laptops): + total_sadness = 0 + From 9ffb839ac35a6fd6c7100128fdaf0ad48adfe845 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 22:47:41 +0000 Subject: [PATCH 10/18] compute sadness level for each scenario --- implement-laptop-allocation/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 2eca7c95..cb824d39 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -41,3 +41,7 @@ def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person for scenario in scenarios(laptops): total_sadness = 0 + #find the sadness level for each scenario + for person, laptop in zip(people, scenario): + total_sadness += sadness(person, laptop) + From 658bbdb6f8c099ac0afeed2685f74f32552d29f7 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 22:50:19 +0000 Subject: [PATCH 11/18] add if condition to find if we're at the best scenario leading to lowest sandess level --- implement-laptop-allocation/main.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index cb824d39..3b0ab6f9 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -36,7 +36,7 @@ def sadness(person: Person, laptop: Laptop) ->int: def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: best_scenario_assignment = None - best_scenario_sadness = float("inf") + best_total_sadness = float("inf") for scenario in scenarios(laptops): total_sadness = 0 @@ -45,3 +45,9 @@ def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person for person, laptop in zip(people, scenario): total_sadness += sadness(person, laptop) + #Check if this is the best scenario so far + if total_sadness < best_total_sadness: + best_total_sadness = total_sadness + best_scenario_assignment = scenario + + From 73156dd403c70652fb2bce7160651e4e7470926d Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 22:52:36 +0000 Subject: [PATCH 12/18] adding Dict as import from typing module --- implement-laptop-allocation/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 3b0ab6f9..65d01f92 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from enum import Enum -from typing import List +from typing import List, Dict from itertools import permutations From f599d9b65da0bcdf32cfc5a8204c174c0170de6e Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 22:56:29 +0000 Subject: [PATCH 13/18] convert best_scenario_Assignment to a dictionary {perspn:laptop} --- implement-laptop-allocation/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 65d01f92..7a226a73 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -50,4 +50,7 @@ def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person best_total_sadness = total_sadness best_scenario_assignment = scenario + # Convert best_scenario_assignment into Dict[Person, Laptop] + return {person: laptop for person, laptop in zip(people, best_scenario_assignment)} + From f39df4a5ed202bb791f07505b7957cd67c9ef39e Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Mon, 8 Dec 2025 22:58:33 +0000 Subject: [PATCH 14/18] fix buggy code by replacing scenarios with permutations --- implement-laptop-allocation/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 7a226a73..61ea5f57 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -38,7 +38,7 @@ def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person best_scenario_assignment = None best_total_sadness = float("inf") - for scenario in scenarios(laptops): + for scenario in permutations(laptops): total_sadness = 0 #find the sadness level for each scenario From b6e2e467a88943a0e72131216ea0015575d01f23 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Fri, 16 Jan 2026 15:31:26 +0000 Subject: [PATCH 15/18] Renamed list "preferred_operating_systems" with a plural name for better readability --- implement-laptop-allocation/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 61ea5f57..2f177a24 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -14,7 +14,7 @@ class Person: name: str age: int # Sorted in order of preference, most preferred is first. - preferred_operating_system: List[OperatingSystem] + preferred_operating_systems: List[OperatingSystem] @dataclass(frozen=True) @@ -28,7 +28,7 @@ class Laptop: def sadness(person: Person, laptop: Laptop) ->int: try: - return person.preferred_operating_system.index(laptop.operating_system) + return person.preferred_operating_systems.index(laptop.operating_system) except ValueError: return 100 From 12da74e759306923ca7eaf36a7edc5025cd033d8 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Fri, 16 Jan 2026 15:39:28 +0000 Subject: [PATCH 16/18] remove the exception handling with a simple if condition to avoid complication and unwanted inefficiencies --- implement-laptop-allocation/main.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 2f177a24..3d10e4ad 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -27,9 +27,8 @@ class Laptop: def sadness(person: Person, laptop: Laptop) ->int: - try: - return person.preferred_operating_systems.index(laptop.operating_system) - except ValueError: + if laptop.operating_system in person.preferred_operating_systems: + return person.preferred_operating_systems.index(laptop.operating_system) return 100 From 7fc8382b2e8253ef1db11cee96d1d0dd491931e5 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Fri, 16 Jan 2026 15:52:36 +0000 Subject: [PATCH 17/18] change logic by implementing break once a scenario is already worse than best --- implement-laptop-allocation/main.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index 3d10e4ad..f76cc689 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -44,11 +44,16 @@ def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person for person, laptop in zip(people, scenario): total_sadness += sadness(person, laptop) - #Check if this is the best scenario so far + #Implement early break if total is already worse than best + if total_sadness >= best_total_sadness: + break + + # Check if this scenario is better if total_sadness < best_total_sadness: - best_total_sadness = total_sadness - best_scenario_assignment = scenario + best_total_sadness = total_sadness + best_scenario_assignment = scenario + # Convert best_scenario_assignment into Dict[Person, Laptop] return {person: laptop for person, laptop in zip(people, best_scenario_assignment)} From cc37f86d505fd065d2efe2ad77770c3408123e13 Mon Sep 17 00:00:00 2001 From: HassanOHOsman Date: Sun, 18 Jan 2026 20:58:19 +0000 Subject: [PATCH 18/18] Replace current laptop allocation with Hungarian algorithm --- implement-laptop-allocation/main.py | 105 +++++++++++++++++++++------- 1 file changed, 79 insertions(+), 26 deletions(-) diff --git a/implement-laptop-allocation/main.py b/implement-laptop-allocation/main.py index f76cc689..380ea2c5 100644 --- a/implement-laptop-allocation/main.py +++ b/implement-laptop-allocation/main.py @@ -1,7 +1,9 @@ from dataclasses import dataclass from enum import Enum from typing import List, Dict -from itertools import permutations + +from scipy.optimize import linear_sum_assignment + class OperatingSystem(Enum): @@ -33,28 +35,79 @@ def sadness(person: Person, laptop: Laptop) ->int: def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]: - - best_scenario_assignment = None - best_total_sadness = float("inf") - - for scenario in permutations(laptops): - total_sadness = 0 - - #find the sadness level for each scenario - for person, laptop in zip(people, scenario): - total_sadness += sadness(person, laptop) - - #Implement early break if total is already worse than best - if total_sadness >= best_total_sadness: - break - - # Check if this scenario is better - if total_sadness < best_total_sadness: - best_total_sadness = total_sadness - best_scenario_assignment = scenario - - - # Convert best_scenario_assignment into Dict[Person, Laptop] - return {person: laptop for person, laptop in zip(people, best_scenario_assignment)} - - + + if len(people) != len(laptops): + raise ValueError("Number of individuals and laptops must be equal") + + cost_matrix = [ + [sadness(person, laptop) for laptop in laptops] + for person in people + ] + + person_indices, laptop_indices = linear_sum_assignment(cost_matrix) + + return {people[p_idx]: laptops[l_idx] + for p_idx, l_idx in zip(person_indices, laptop_indices) + + } + + + + + +if __name__ == "__main__": + people = [ + Person( + name="Alice", + age=30, + preferred_operating_systems=[ + OperatingSystem.MACOS, + OperatingSystem.UBUNTU, + ], + ), + Person( + name="Bob", + age=25, + preferred_operating_systems=[ + OperatingSystem.UBUNTU, + OperatingSystem.MACOS, + ], + ), + Person( + name="Carol", + age=28, + preferred_operating_systems=[ + OperatingSystem.ARCH, + OperatingSystem.MACOS, + ], + ), + ] + + laptops = [ + Laptop( + id=1, + manufacturer="Apple", + model="MacBook Air", + screen_size_in_inches=13.3, + operating_system=OperatingSystem.MACOS, + ), + Laptop( + id=2, + manufacturer="Dell", + model="XPS", + screen_size_in_inches=13.0, + operating_system=OperatingSystem.UBUNTU, + ), + Laptop( + id=3, + manufacturer="Lenovo", + model="ThinkPad", + screen_size_in_inches=14.0, + operating_system=OperatingSystem.ARCH, + ), + ] + + allocation = allocate_laptops(people, laptops) + + for person, laptop in allocation.items(): + print(f"{person.name} → {laptop.model} ({laptop.operating_system.value})")