11from dataclasses import dataclass
22from enum import Enum
33from typing import List , Dict
4- from itertools import permutations
4+ import numpy as np
5+ from scipy .optimize import linear_sum_assignment
56
67unpreferred_OS_penalty = 100
78
@@ -14,7 +15,7 @@ class OperatingSystem(Enum):
1415class Person :
1516 name : str
1617 age : int
17- preferred_operating_system : tuple
18+ preferred_operating_system : tuple
1819
1920@dataclass (frozen = True )
2021class Laptop :
@@ -27,33 +28,30 @@ class Laptop:
2728def allocate_laptops (people : List [Person ], laptops : List [Laptop ]) -> Dict [Person , Laptop ]:
2829 if len (people ) != len (laptops ):
2930 raise ValueError ("Number of people must match number of laptops" )
31+
32+ n = len (people )
33+ cost_matrix = np .zeros ((n , n ), dtype = int )
3034
31- best_assignment = None
32- lowest_sadness = float ("inf" )
33-
34- for perm in permutations (laptops ):
35- total_sadness = 0
36- for person , laptop in zip (people , perm ):
35+ for i , person in enumerate (people ):
36+ for j , laptop in enumerate (laptops ):
3737 if laptop .operating_system in person .preferred_operating_system :
38- sadness = person .preferred_operating_system .index (laptop .operating_system )
38+ cost_matrix [ i , j ] = person .preferred_operating_system .index (laptop .operating_system )
3939 else :
40- sadness = unpreferred_OS_penalty
41- total_sadness += sadness
42-
43- if total_sadness < lowest_sadness :
44- lowest_sadness = total_sadness
45- best_assignment = perm
40+ cost_matrix [i , j ] = unpreferred_OS_penalty
4641
47- return { person : laptop for person , laptop in zip ( people , best_assignment )}
42+ person_indices , laptop_indices = linear_sum_assignment ( cost_matrix )
4843
44+ return {
45+ people [i ]: laptops [j ] for i , j in zip (person_indices , laptop_indices )
46+ }
4947laptops = [
5048 Laptop (1 , "Dell" , "XPS 13" , 13 , OperatingSystem .ARCH ),
5149 Laptop (2 , "HP" , "Spectre 15" , 15 , OperatingSystem .UBUNTU ),
5250 Laptop (3 , "Lenovo" , "ThinkPad 14" , 14 , OperatingSystem .UBUNTU ),
5351 Laptop (4 , "Apple" , "MacBook Air" , 13 , OperatingSystem .MACOS ),
5452 Laptop (5 , "Apple" , "MacBook Pro" , 16 , OperatingSystem .MACOS ),
5553 Laptop (6 , "Dell" , "Latitude" , 15 , OperatingSystem .ARCH ),
56- Laptop (7 , "HP" , "EliteBook" , 13 , OperatingSystem .MACOS ),
54+ Laptop (7 , "HP" , "EliteBook" , 13 , OperatingSystem .MACOS ), # Reviewer joke: HP running macOS 😄
5755 Laptop (8 , "Lenovo" , "Yoga" , 14 , OperatingSystem .UBUNTU )
5856]
5957
@@ -70,19 +68,20 @@ def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person
7068
7169assignment = allocate_laptops (people , laptops )
7270
73-
71+ print ( "Laptop Assignments: \n " )
7472for person , laptop in assignment .items ():
75- person_sadness_score = (
73+ sadness = (
7674 person .preferred_operating_system .index (laptop .operating_system )
7775 if laptop .operating_system in person .preferred_operating_system
7876 else unpreferred_OS_penalty
7977 )
80- print (f"{ person .name } was allocated { laptop .manufacturer } { laptop .model } "
81- f"with { laptop .operating_system .value } ( Score: { person_sadness_score } ) " )
78+ print (f"{ person .name } → { laptop .manufacturer } { laptop .model } "
79+ f"( { laptop .operating_system .value } ) | Score: { sadness } " )
8280
8381total_sadness = sum (
8482 person .preferred_operating_system .index (laptop .operating_system )
8583 if laptop .operating_system in person .preferred_operating_system else unpreferred_OS_penalty
8684 for person , laptop in assignment .items ()
8785)
86+
8887print ("\n Total sadness:" , total_sadness )
0 commit comments