计算调度问题中的雇用天数
Calculate hired days in a scheduling problem
在调度问题中,我还想最小化总雇佣天数。
A employee is hired in a given day if he/she works before that day and also after that day.
这是一个小的工作示例:
import random
from ortools.sat.python import cp_model
model = cp_model.CpModel()
solver = cp_model.CpSolver()
employees = range(3)
days = range(10)
works_day = {(e, d): model.NewBoolVar(f'{e}_works_{d}')
for e in employees for d in days}
hired_day = {(e, d): model.NewBoolVar(f'{e}_employed_{d}')
for e in employees for d in days}
# random example
for boolean in works_day.values():
model.Add(boolean == random.choice([0, 1]))
# give value to hired_day
add_hired_days()
# solve
print('Variables:', len(model.Proto().variables))
print('Constraints:', len(model.Proto().constraints))
status = solver.Solve(model)
for e in employees:
print()
print('Employee', e)
for d in days:
print('Works', solver.Value(works_day[e, d]),
'Hired', solver.Value(hired_day[e, d]))
其中 add_hired_days
是:
def add_hired_days():
for idx, d in enumerate(days):
for e in employees:
model.AddImplication(works_day[e, d], hired_day[e, d])
previous = [works_day[e, d] for d in days[:idx + 1]]
following = [works_day[e, d] for d in days[idx:]]
# too many variables
works_previous = model.NewBoolVar('')
works_following = model.NewBoolVar('')
model.AddBoolOr(previous).OnlyEnforceIf(works_previous)
model.AddBoolAnd([d.Not() for d in previous
]).OnlyEnforceIf(works_previous.Not())
model.AddBoolOr(following).OnlyEnforceIf(works_following)
model.AddBoolAnd([d.Not() for d in following
]).OnlyEnforceIf(works_following.Not())
model.AddBoolAnd([works_previous, works_following
]).OnlyEnforceIf(hired_day[e, d])
model.AddBoolOr([works_previous.Not(),
works_following.Not()
]).OnlyEnforceIf(hired_day[e, d].Not())
有没有办法在不创建这么多变量和约束的情况下做到这一点?
如果一个员工一个月工作n天,他需要被雇佣n - 2次。
按照 Laurent 的建议解决。
import random
from ortools.sat.python import cp_model
if __name__ == '__main__':
model = cp_model.CpModel()
solver = cp_model.CpSolver()
employees = range(3)
days = range(10)
horizon = len(days) - 1
works_day = {(e, d): model.NewBoolVar(f'{e}_works_{d}')
for e in employees for d in days}
hired_days = [
model.NewIntVar(0, len(days), f'{e}_hired') for e in employees
]
first_day = [
model.NewIntVar(0, horizon, f'{e}_first_day') for e in employees
]
last_day = [
model.NewIntVar(0, horizon, f'{e}_last_day') for e in employees
]
# random example
for boolean in works_day.values():
model.Add(boolean == random.choice([0, 1]))
for e in employees:
v1 = [model.NewIntVar(0, horizon, '') for _ in days]
v2 = [model.NewIntVar(0, horizon, '') for _ in days]
for d in days:
model.Add(v1[d] == d * works_day[e, d])
model.Add(v2[d] == horizon + works_day[e, d] * (d - horizon))
model.AddMinEquality(first_day[e], v2)
model.AddMaxEquality(last_day[e], v1)
model.Add(hired_days[e] == last_day[e] - first_day[e] + 1)
# solve
status = solver.Solve(model)
for e in employees:
print()
print('Employee', e)
for d in days:
print('Works', solver.Value(works_day[e, d]))
print('First day:', solver.Value(first_day[e]), 'Last day:',
solver.Value(last_day[e]), 'Hired:', solver.Value(hired_days[e]))
在调度问题中,我还想最小化总雇佣天数。
A employee is hired in a given day if he/she works before that day and also after that day.
这是一个小的工作示例:
import random
from ortools.sat.python import cp_model
model = cp_model.CpModel()
solver = cp_model.CpSolver()
employees = range(3)
days = range(10)
works_day = {(e, d): model.NewBoolVar(f'{e}_works_{d}')
for e in employees for d in days}
hired_day = {(e, d): model.NewBoolVar(f'{e}_employed_{d}')
for e in employees for d in days}
# random example
for boolean in works_day.values():
model.Add(boolean == random.choice([0, 1]))
# give value to hired_day
add_hired_days()
# solve
print('Variables:', len(model.Proto().variables))
print('Constraints:', len(model.Proto().constraints))
status = solver.Solve(model)
for e in employees:
print()
print('Employee', e)
for d in days:
print('Works', solver.Value(works_day[e, d]),
'Hired', solver.Value(hired_day[e, d]))
其中 add_hired_days
是:
def add_hired_days():
for idx, d in enumerate(days):
for e in employees:
model.AddImplication(works_day[e, d], hired_day[e, d])
previous = [works_day[e, d] for d in days[:idx + 1]]
following = [works_day[e, d] for d in days[idx:]]
# too many variables
works_previous = model.NewBoolVar('')
works_following = model.NewBoolVar('')
model.AddBoolOr(previous).OnlyEnforceIf(works_previous)
model.AddBoolAnd([d.Not() for d in previous
]).OnlyEnforceIf(works_previous.Not())
model.AddBoolOr(following).OnlyEnforceIf(works_following)
model.AddBoolAnd([d.Not() for d in following
]).OnlyEnforceIf(works_following.Not())
model.AddBoolAnd([works_previous, works_following
]).OnlyEnforceIf(hired_day[e, d])
model.AddBoolOr([works_previous.Not(),
works_following.Not()
]).OnlyEnforceIf(hired_day[e, d].Not())
有没有办法在不创建这么多变量和约束的情况下做到这一点?
如果一个员工一个月工作n天,他需要被雇佣n - 2次。
按照 Laurent 的建议解决。
import random
from ortools.sat.python import cp_model
if __name__ == '__main__':
model = cp_model.CpModel()
solver = cp_model.CpSolver()
employees = range(3)
days = range(10)
horizon = len(days) - 1
works_day = {(e, d): model.NewBoolVar(f'{e}_works_{d}')
for e in employees for d in days}
hired_days = [
model.NewIntVar(0, len(days), f'{e}_hired') for e in employees
]
first_day = [
model.NewIntVar(0, horizon, f'{e}_first_day') for e in employees
]
last_day = [
model.NewIntVar(0, horizon, f'{e}_last_day') for e in employees
]
# random example
for boolean in works_day.values():
model.Add(boolean == random.choice([0, 1]))
for e in employees:
v1 = [model.NewIntVar(0, horizon, '') for _ in days]
v2 = [model.NewIntVar(0, horizon, '') for _ in days]
for d in days:
model.Add(v1[d] == d * works_day[e, d])
model.Add(v2[d] == horizon + works_day[e, d] * (d - horizon))
model.AddMinEquality(first_day[e], v2)
model.AddMaxEquality(last_day[e], v1)
model.Add(hired_days[e] == last_day[e] - first_day[e] + 1)
# solve
status = solver.Solve(model)
for e in employees:
print()
print('Employee', e)
for d in days:
print('Works', solver.Value(works_day[e, d]))
print('First day:', solver.Value(first_day[e]), 'Last day:',
solver.Value(last_day[e]), 'Hired:', solver.Value(hired_days[e]))