Skip to content

Commit 259f7b5

Browse files
committed
Replace brute-force laptop allocation with scalable greedy approach; Remove unwanted imports
1 parent 81c5477 commit 259f7b5

1 file changed

Lines changed: 35 additions & 33 deletions

File tree

sprint-5-laptop-allocation/laptop_allocation.py

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
from dataclasses import dataclass
22
from enum import Enum
3-
from typing import List, Dict, Optional, Tuple
4-
import itertools
3+
from typing import List, Dict, Optional
54
import sys
65

76
class OperatingSystem(Enum):
87
MACOS = "macOS"
98
ARCH = "Arch Linux"
109
UBUNTU = "Ubuntu"
11-
1210
@dataclass(frozen=True)
1311
class Person:
1412
name: str
@@ -18,8 +16,6 @@ class Person:
1816
# custom hash to hash the immutable version of the list
1917
def __hash__(self) -> int:
2018
return hash((self.name, self.age, tuple(self.preferred_operating_system)))
21-
22-
2319
@dataclass(frozen=True)
2420
class Laptop:
2521
id: int
@@ -30,22 +26,23 @@ class Laptop:
3026

3127
# =====define parse operating system======
3228
def parse_operating_system(raw: str) -> OperatingSystem:
33-
normalized = raw.strip().lower()
29+
normalized = raw.strip().lower()
3430

35-
aliases = {
36-
"macos": OperatingSystem.MACOS,
37-
"mac": OperatingSystem.MACOS,
38-
"arch": OperatingSystem.ARCH,
39-
"arch linux": OperatingSystem.ARCH,
40-
"ubuntu": OperatingSystem.UBUNTU,
41-
}
31+
aliases = {
32+
"macos": OperatingSystem.MACOS,
33+
"mac": OperatingSystem.MACOS,
34+
"arch": OperatingSystem.ARCH,
35+
"arch linux": OperatingSystem.ARCH,
36+
"ubuntu": OperatingSystem.UBUNTU,
37+
}
4238

43-
if normalized not in aliases:
44-
valid = ", ".join(sorted(aliases.keys()))
45-
print(f"Error: invalid operating system. Try one of: {valid}", file=sys.stderr)
46-
sys.exit(1)
4739

48-
return aliases[normalized]
40+
if normalized not in aliases:
41+
valid = ", ".join(sorted(aliases.keys()))
42+
print(f"Error: invalid operating system. Try one of: {valid}", file=sys.stderr)
43+
sys.exit(1)
44+
45+
return aliases[normalized]
4946

5047
# ======= sadness helper ============
5148
def sadness(person: Person, laptop: Laptop) -> int:
@@ -60,25 +57,30 @@ def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person
6057
if len(laptops) < len(people):
6158
raise ValueError("Not enough laptops to allocate one per person.")
6259

63-
best_assignment: Optional[Dict[Person, Laptop]] = None
64-
best_total_sadness: Optional[int] = None
60+
sorted_people = sorted(people, key=lambda p: len(p.preferred_operating_system))
61+
62+
remaining_laptops = laptops[:]
63+
assignment: Dict[Person, Laptop] = {}
64+
65+
for person in sorted_people:
66+
best_laptop:Optional[Laptop] = None
67+
best_score = 10**9
6568

66-
for chosen_laptops in itertools.combinations(laptops, len(people)):
67-
for permuted_laptops in itertools.permutations(chosen_laptops):
68-
total = 0
69-
for i in range(len(people)):
70-
person = people[i]
71-
laptop = permuted_laptops[i]
72-
total += sadness(person, laptop)
69+
for laptop in remaining_laptops:
70+
score = sadness(person, laptop)
71+
if score < best_score:
72+
best_score = score
73+
best_laptop = laptop
74+
if score == 0:
75+
break
7376

74-
if best_total_sadness is None or total < best_total_sadness:
75-
best_total_sadness = total
76-
best_assignment = {people[i]: permuted_laptops[i] for i in range(len(people))}
77+
if best_laptop is None:
78+
raise RuntimeError("Allocation failed unexpectedly.")
7779

78-
if best_assignment is None:
79-
raise RuntimeError("Allocation failed unexpectedly.")
80+
assignment[person] = best_laptop
81+
remaining_laptops.remove(best_laptop)
8082

81-
return best_assignment
83+
return assignment
8284

8385
# ======define main ================
8486
def main() -> None:

0 commit comments

Comments
 (0)