무엇을 정하나요
직원마다 처리할 업무 목록과 순서를 만듭니다. 목록의 업무는 쉬지 않고 이어서 처리합니다.
최소 실행 예제
from optimaru import task_assigning as ta
from optimaru.task_assigning.models import (
CustomerModel, EmployeeModel, TaskAssigningSolutionModel, TaskModel, TaskTypeModel,
)
problem = TaskAssigningSolutionModel(
task_types=[
TaskTypeModel(code="DOC", base_duration=30, required_skills=["writing"]),
TaskTypeModel(code="FIX", base_duration=60, required_skills=["python"]),
],
customers=[CustomerModel(id="ACME")],
tasks=[
TaskModel(id="T1", task_type_code="DOC", customer_id="ACME", priority="CRITICAL"),
TaskModel(id="T2", task_type_code="FIX", customer_id="ACME", priority="MAJOR"),
TaskModel(id="T3", task_type_code="FIX", customer_id="ACME"),
],
employees=[
EmployeeModel(id="E1", skills=["writing"], customer_to_affinity={"ACME": "HIGH"}),
EmployeeModel(id="E2", skills=["python"], customer_to_affinity={"ACME": "MEDIUM"}),
],
)
result = ta.solve(problem, time_limit_seconds=2.0, random_seed=42)
print(result.score_hard, result.score_soft0, result.score_soft1, result.score_soft2)
print({e.id: e.tasks for e in result.employees})
print({t.id: t.start_time for t in result.tasks})
같은 유스케이스를 그림과 실행 결과가 담긴 Jupyter 노트북으로도 볼 수 있습니다. examples/task-assigning.ipynb ↗
TaskTypeModel
code 필수 입력 | 업무 유형 id입니다. |
base_duration 필수 입력 | 숙련도 배수를 곱하기 전의 기본 시간(분)입니다. |
required_skills | 이 유형에 필요한 역량입니다. |
CustomerModel
id 필수 입력 | 고객 id입니다. |
TaskModel
id 필수 입력 | 업무 id입니다. |
task_type_code 필수 입력 | 이 업무의 TaskTypeModel입니다. |
customer_id 필수 입력 | 이 업무의 고객입니다. |
min_start_time | 가장 이른 시작 시각(분)입니다. |
priority | MINOR, MAJOR, CRITICAL 중 하나입니다. 기본값은 MINOR입니다. |
EmployeeModel
id 필수 입력 | 직원 id입니다. |
skills | 직원이 가진 역량입니다. |
customer_to_affinity | 고객 id별 숙련도입니다. HIGH, MEDIUM, LOW, NONE 중 하나이고, 없는 고객은 NONE으로 봅니다. |
TaskAssigningSolutionModel
task_types 필수 입력 | 업무 유형 목록입니다. |
customers 필수 입력 | 고객 목록입니다. |
tasks 필수 입력 | 업무 목록입니다. |
employees 필수 입력 | 직원 목록입니다. |
풀이 파라미터
풀이를 호출할 때 넘기는 값입니다. 시간 제한, 시드, 병렬 수가 여기에 있습니다.
탐색 옵션 전체 (23)
SolverConfig에 값을 넣어 config=로 넘기세요.
돌아오는 결과
solve는 모델을 받아 풀린 모델을 돌려줍니다.
EmployeeModel
tasks | 이 직원이 처리할 업무 id를 순서대로 나열한 목록입니다. |
TaskModel
start_time | 계산된 시작 시각(분)입니다. 미배정이면 None입니다. |
TaskAssigningSolutionModel
score_hard | 모자란 역량 수의 음수입니다. 0이면 모든 업무가 담당자의 역량에 맞습니다. |
score_soft0 | 미배정 업무 수의 음수입니다. |
score_soft1 | squared_makespan_cost 값의 음수입니다. |
score_soft2 | 우선순위별 종료 시각 값의 음수입니다. |
제약
optimaru.task_assigning.constraints에서 클래스를 가져와 RequiredSkillCost()처럼 호출해서 넘깁니다. 넘긴 제약만 점수에 들어가고, 아무것도 넘기지 않으면 기본 제약을 씁니다. 목록에 같은 제약을 여러 번 넣을 수 있으며, 넣은 것마다 따로 점수에 들어갑니다. 제약마다 다음 파라미터를 받습니다.
역량 부족은 필수, 미배정 업무는 중간, 나머지는 선호 수준입니다. 중간 수준은 필수 점수 바로 아래 단위로 합쳐지므로, 미배정 업무가 1e9개 미만이면 역량 부족 하나가 언제나 더 무겁습니다. squared_makespan_cost는 기본 가중치가 1e7이어서 우선순위별 종료 시각보다 앞섭니다. 직접 준 가중치는 적용되는 수준의 단위로 세며, level로 어느 제약이든 세 수준 가운데 하나로 옮길 수 있습니다. 이렇게 합치기 때문에 필수 수준에는 가중치를 곱한 값으로 약 9.2e9까지만 담을 수 있고, 그보다 커지면 필수 점수가 한계값에 머물러 탐색이 해를 구별하지 못합니다. 그러니 squared_makespan_cost를 필수로 바꿀 때는 weight=1을 주세요. 가중치가 1e7이면 대부분의 문제에서 이 한도를 넘습니다.
위의 최소 실행 예제에 이어서 실행합니다.
from optimaru.task_assigning.constraints import (
CriticalPriorityTaskEndTimeCost, RequiredSkillCost, UnassignedTasksCost, get_constraints,
)
picked = get_constraints([
RequiredSkillCost(), UnassignedTasksCost(), CriticalPriorityTaskEndTimeCost(weight=20),
])
custom = ta.solve(problem, time_limit_seconds=2.0, random_seed=42, constraints=picked)
print(custom.score_hard, custom.score_soft2)
제약 사용법 →
기존 해에서 시작하기
최신 버전이 필요합니다. pip install -U optimaru로 올려 주세요.
with_initial_solution(problem, solution)은 직원의 tasks를 해의 값으로 바꾼 새 문제를 돌려줍니다.
위의 최소 실행 예제에 이어서 실행합니다.
seeded = ta.with_initial_solution(problem, result) # result.model_dump(mode="json")도 됩니다
better = ta.solve(seeded, time_limit_seconds=2.0, random_seed=42)
print(better.score_hard, better.score_soft1, better.score_soft2)
- 완전한 해를 넣으면 구성 단계를 건너뛰고 그 해에서 바로 탐색을 시작합니다. 일부만 넣으면 넣은 부분은 그대로 두고, 빠진 업무만 새로 구성합니다.
time_limit_seconds=0을 주면 바로 멈추므로 완전한 해는 넣은 그대로 돌아옵니다. step_count_limit = 0은 제한이 없다는 뜻이므로, 바로 멈추려면 시간 제한을 쓰세요.- 모르는 id가 있거나 같은 업무가 두 번 나오면
ValueError가 납니다.
예외와 제한
- 업무 시간은
base_duration에 숙련도 배수를 곱한 값입니다. HIGH 1, MEDIUM 2, LOW 3, NONE 4입니다. - 모든 업무는 결국 배정됩니다. 맡을 수 있는 직원이 없는 업무도 미배정으로 남지 않고, 필수 벌점을 받은 채 배정됩니다.
- 시각은 분 단위 정수입니다.
직접 해 보기