线性求和分配(SciPy)和平衡成本

cbwuti44  于 2022-11-10  发布在  其他
关注(0)|答案(1)|浏览(203)

我在使用scipy.optimize.linear_sum_assignment将任务(成本)平均分配给员工时遇到了困难,因为每个员工可以分配多个任务。成本矩阵表示每个员工的每个任务的工作量。
我们希望最小化所有工人的总成本,同时平均分配每个工人的成本。
在本例中,我们有3个名为abc的工作人员。每个工作人员总共可以分配4个任务,因此在成本矩阵中,我们有座席a_1a_2等。
linear_sum_assignment给予了总成本最小化的任务,为了简单起见,我们的例子使用了一个成本矩阵,使得任何任务都能给出相同的总成本。
但是,成本并不是平均分配给3个员工。在我们的示例中,3个员工的成本分别为65163192
是否有可能尽可能地最小化成本,同时在3个工人之间更均匀地分配每个工人的成本?

from scipy.optimize import linear_sum_assignment
import numpy as np

worker_capacities = [
    "a_1", "a_2", "a_3", "a_4",
    "b_1", "b_2", "b_3", "b_4",
    "c_1", "c_2", "c_3", "c_4",
]
n_tasks = len(worker_capacities)
c = np.array([
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
 [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
])

_, assignments = linear_sum_assignment(c)
print("Costs for worker a:", sum(c[i][j] for i, j in enumerate(assignments[0:4])))
print("Costs for worker b:", sum(c[i+4][j] for i, j in enumerate(assignments[4:8])))
print("Costs for worker c:", sum(c[i+8][j] for i, j in enumerate(assignments[8:12])))

给出输出

Costs for worker a: 65
Costs for worker b: 163
Costs for worker c: 192
s3fp2yjn

s3fp2yjn1#

linear_sum_assignment方法不支持约束或自定义目标,因此我认为这是不可能的。
然而,您可以将问题表述为混合整数线性规划问题(MILP),并通过PuLP 1来求解。为了平均分配每个工人的总成本,您可以使每个工人的最大总成本与最小总成本之间的差异最小化。以下是一个可能的表述:

Sets:

- workers = ["a", "b", "c"]
- tasks   = [1, 2, ..., 12]

Variables:

- x[w,t] = 1 iff worker w is assigned to task t, 0 otherwise
- min_val
- max_val

Model:

min  max_val - min_val

s.t. 

# each worker is assigned to exactly n_tasks_per_worker tasks

sum(x[w,t] for t in tasks) == n_tasks_per_worker for all w in workers

# each task can only be assigned once

sum(x[w,t] for w in workers) == 1 for all t in tasks

# evenly distribute the tasks

sum(x[w,t] for t in tasks) <= max_val for all w in workers
sum(x[w,t] for t in tasks) >= min_val for all w in workers

代码很简单:

import pulp
import numpy as np

workers = ["a", "b", "c"]
n_workers = len(workers)
n_tasks_per_worker = 4
n_tasks = n_workers * n_tasks_per_worker

c = np.array([[27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
              [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26],
              [27, 42, 65, 33, 67, 45, 60, 76,  6,  6, 43, 26]])

# create the model

mdl = pulp.LpProblem("even_assignment")

# decision variables

x = {}
for w in workers:
    for t in range(n_tasks):
        x[w, t] = pulp.LpVariable(f"x[{w}, {t}]", cat="Binary")

max_val = pulp.LpVariable("max_val", cat="Continuous")
min_val = pulp.LpVariable("min_val", cat="Continuous")

# objective: minimize the difference between the maximum and the minimum

# costs per worker

mdl.setObjective(max_val - min_val)

# constraint: each worker gets assigned exactly n_tasks_per_worker

for w in workers:
    mdl.addConstraint(sum(x[w, task] for task in range(n_tasks)) == n_tasks_per_worker)

# constraint: each task can only be assigned once

for task in range(n_tasks):
    mdl.addConstraint(sum(x[w, task] for w in workers) == 1)

# constraint: evenly distribute the tasks

for i_w, w in enumerate(workers):
    assignment_cost = sum(x[w,task]*c[i_w,task] for task in range(n_tasks))
    mdl.addConstraint(assignment_cost <= max_val)
    mdl.addConstraint(assignment_cost >= min_val)

# solve the problem

mdl.solve()

# Output

for i_w, w in enumerate(workers):
    worker_cost = sum(x[w, t].varValue*c[i_w, t] for t in range(n_tasks))
    print(f"costs for worker {w}: {worker_cost:.2f}")

这给了我

costs for worker a: 165.00
costs for worker b: 167.00
costs for worker c: 164.00

[1]确切地说,PuLP不是一个解算器,它只是一个建模框架,可以将MILP传递给MILP解算器,如CBC、SCIP、HiGHS等。

相关问题