Compare commits

...

2 commits

Author SHA1 Message Date
Théophile Bastian 07d3ce14e7 Partition: avoid tasks too close in time 2023-03-08 11:16:32 +01:00
Théophile Bastian 71bc85cd3a Update costs 2023-03-08 11:16:32 +01:00
3 changed files with 35 additions and 13 deletions

View file

@ -75,6 +75,7 @@ def assigner_taches(root_task: Category | Task, group_count: int):
multiplicity[t_id] = task.nb_groups multiplicity[t_id] = task.nb_groups
repart: list[list[TaskId]] = partition( repart: list[list[TaskId]] = partition(
bin_count=group_count, bin_count=group_count,
tasks=all_tasks,
costs=costs, costs=costs,
multiplicity=multiplicity, multiplicity=multiplicity,
) )

View file

@ -1,12 +1,16 @@
""" Implements Multiway number partitioning greedy algorithm """ """ Implements Multiway number partitioning greedy algorithm """
import typing as t import typing as t
import logging
from sortedcontainers import SortedList from sortedcontainers import SortedList
from .config import Task
__all__ = ["TaskId", "partition"] __all__ = ["TaskId", "partition"]
TaskId = t.NewType("TaskId", int) TaskId = t.NewType("TaskId", int)
logger = logging.getLogger(__name__)
class PartitionException(Exception): class PartitionException(Exception):
"""An exception occurring during partitioning""" """An exception occurring during partitioning"""
@ -36,7 +40,10 @@ class Bin:
def partition( def partition(
bin_count: int, costs: dict[TaskId, int], multiplicity: dict[TaskId, int] bin_count: int,
tasks: list[Task],
costs: dict[TaskId, int],
multiplicity: dict[TaskId, int],
) -> list[list[TaskId]]: ) -> list[list[TaskId]]:
"""Partitions the tasks, each with cost `costs[i]`, into `bin_count` bins. Each """Partitions the tasks, each with cost `costs[i]`, into `bin_count` bins. Each
task has multiplicity `multiplicity[i]`, copies of the same task being mutually task has multiplicity `multiplicity[i]`, copies of the same task being mutually
@ -50,22 +57,36 @@ def partition(
ordered_tasks.sort(key=lambda x: costs[x], reverse=True) ordered_tasks.sort(key=lambda x: costs[x], reverse=True)
for task in ordered_tasks: for task in ordered_tasks:
least_full: Bin possible_bins: list[int] = []
least_full_pos: int min_possible: t.Optional[int] = None
for pos, cur_bin in enumerate(bins): for pos, cur_bin in enumerate(bins):
if task not in cur_bin: if min_possible is not None and cur_bin.cost > min_possible:
least_full = cur_bin
least_full_pos = pos
break break
else: if task not in cur_bin:
if min_possible is None:
min_possible = cur_bin.cost
possible_bins.append(pos)
if not possible_bins:
raise UnsolvableConflict( raise UnsolvableConflict(
"Pas assez de groupes pour affecter la tâche " "Pas assez de groupes pour affecter la tâche "
+ f"{task} {multiplicity[task]} fois." + f"{tasks[task].qualified_name} {multiplicity[task]} fois."
) )
del bins[least_full_pos] # Pick one of the groups -- maximize distance to other tasks
least_full.add(task, costs[task]) closest_assigned = []
bins.add(least_full) for cur_bin_id in possible_bins:
cur_bin = bins[cur_bin_id]
if cur_bin.elts:
closest_assigned.append(min((abs(elt - task) for elt in cur_bin.elts)))
else:
closest_assigned.append(len(costs) + 1)
assign_to_bin_id, _ = max(enumerate(closest_assigned), key=lambda x: x[1])
assign_to_bin = bins[possible_bins[assign_to_bin_id]]
del bins[assign_to_bin_id]
assign_to_bin.add(task, costs[task])
bins.add(assign_to_bin)
out: list[list[TaskId]] = [] out: list[list[TaskId]] = []
for cur_bin in bins: for cur_bin in bins:

View file

@ -124,11 +124,11 @@ taches:
taches: taches:
- nom: Nettoyer salles de bain étage - nom: Nettoyer salles de bain étage
descr: Nettoyer les salles de bain du 1er étage du gîte principal descr: Nettoyer les salles de bain du 1er étage du gîte principal
penible: 20 penible: 25
notes: "Remplir les seaux de produit dans la salle plonge" notes: "Remplir les seaux de produit dans la salle plonge"
- nom: Nettoyer salles de bain RdC - nom: Nettoyer salles de bain RdC
descr: Nettoyer les salles de bain du rez-de-chaussée du gîte principal descr: Nettoyer les salles de bain du rez-de-chaussée du gîte principal
penible: 15 penible: 20
notes: "Remplir les seaux de produit dans la salle plonge" notes: "Remplir les seaux de produit dans la salle plonge"
- nom: Serpillère RdC - nom: Serpillère RdC
penible: 15 penible: 15