Solving Complex Scheduling Problems with OR-Tools: A Practical Case Study


foreword

Scheduling problem is an important and common combinatorial optimization problem, which involves reasonably arranging the sequence and time of tasks or activities to optimize some objective function under the constraints of limited resources and time. Scheduling problems are widely used in manufacturing, logistics and transportation, project management, computer task scheduling, job scheduling and other fields.

1. Introduction to Scheduling Problems

Scheduling problem is an important and common combinatorial optimization problem, which involves reasonably arranging the sequence and time of tasks or activities to optimize some objective function under the constraints of limited resources and time. Scheduling problems are widely used in manufacturing, logistics and transportation, project management, computer task scheduling, job scheduling and other fields.

Common scheduling problems include:

  • Job Shop Scheduling Problem : Given a set of jobs and a set of machines that can be executed in parallel, each job needs to be completed in a specific order on a different machine. The goal is to minimize the time required to complete all jobs or maximize production efficiency.
  • Flow Shop Scheduling Problem : Similar to the job shop scheduling problem, but in the flow shop scheduling problem, each job needs to pass through a set of machines in the same order.
  • Vehicle Routing Problem : Given a set of customer points and a set of delivery vehicles, the vehicles need to start from a center, visit each customer point, and return to the center in order to minimize the total delivery cost or minimize the total cost of the vehicle. Travel distance.
  • Resource-Constrained Project Scheduling Problem (Resource-Constrained Project Scheduling Problem) : There are multiple tasks in a project, and each task requires specific resources and time to complete. The goal is to minimize the total duration of the project or maximize resource utilization while satisfying resource and time constraints.
  • Multiprocessor Task Scheduling Problem : Assign a set of tasks to multiple processors and try to balance the load to minimize task completion time or maximize processor utilization.
  • Job Allocation Problem : Create a correspondence between workers and tasks to maximize the total value of completed tasks or minimize the total working time of workers.

Methods for solving scheduling problems typically include greedy algorithms, heuristic algorithms, exact optimization algorithms (such as integer linear programming and constraint programming), and metaheuristic algorithms (such as genetic algorithms and simulated annealing algorithms). The complexity of scheduling problems usually depends on the constraints between tasks and resources, and some scheduling problems are NP-hard problems, which means that finding the optimal solution may take exponential time.

2. Case Analysis

2-1. Nurse scheduling problem

Nurse Scheduling Issue : Organizations with staff working multiple shifts need to schedule enough workers for each shift per day. Usually, the schedule will have some restrictions such as "no employee should work two shifts in a row in one shift". Finding a timetable that satisfies all constraints can be computationally difficult.
Nurse Scheduling Questions:

In the next example, the hospital director needs to create a schedule for 4 nurses based on 3-day criteria :

  • Shifts every day, three times a day.
  • Each day, each shift is assigned a nurse, and no nurse works more than one shift.
  • During the 3 days, each nurse was assigned to at least two shifts.

Next, we show assigning nurses to their shifts subject to the following constraints :

  • Each shift is assigned one nurse per day.
  • A nurse will only work one shift a day at most.

Graphical constraints in the code :
insert image description here
insert image description here

The specific code is as follows :

#导入所需库,定义护士数量、轮班次数、天数
from ortools.sat.python import cp_model
num_nurses = 4
num_shifts = 3
num_days = 3
all_nurses = range(num_nurses)
all_shifts = range(num_shifts)
all_days = range(num_days)

# 创建模型、创建Bool类型变量数组(n, d, s)-->(护士编号,哪一天,哪一个轮班),定义了针对护士的轮班分配。
#即如果将 shift s 分配给 d 天中的护士 n,shifts[(n, d, s)] 的值为 1,否则为 0。
model = cp_model.CpModel()
shifts = {
    
    }
for n in all_nurses:
    for d in all_days:
        for s in all_shifts:
            shifts[(n, d, s)] = model.NewBoolVar('shift_n%id%is%i' % (n, d, s))

# 确保每一天的每一个轮班只有一个护士
for d in all_days:
    for s in all_shifts:
        model.AddExactlyOne(shifts[(n, d, s)] for n in all_nurses)

# 确保每一名护士每天最多轮班一次。
for n in all_nurses:
    for d in all_days:
        model.AddAtMostOne(shifts[(n, d, s)] for s in all_shifts)
        
# 尽量分配均匀,设定每个护士应该上的最小轮班次数和最大轮班次数。
min_shifts_per_nurse = (num_shifts * num_days) // num_nurses
if num_shifts * num_days % num_nurses == 0:
    max_shifts_per_nurse = min_shifts_per_nurse
else:
    max_shifts_per_nurse = min_shifts_per_nurse + 1
    
# 指定每一个护士的最小轮班数为2,最大轮班数为3for n in all_nurses:
    shifts_worked = []
    for d in all_days:
        for s in all_shifts:
            shifts_worked.append(shifts[(n, d, s)])
    model.Add(min_shifts_per_nurse <= sum(shifts_worked))
    model.Add(sum(shifts_worked) <= max_shifts_per_nurse)

solver = cp_model.CpSolver()
solver.parameters.linearization_level = 0
# Enumerate all solutions.
solver.parameters.enumerate_all_solutions = True

# 在求解器上注册一个回调,系统会在每个解决方案中调用该回调函数。
class NursesPartialSolutionPrinter(cp_model.CpSolverSolutionCallback):
    """Print intermediate solutions."""

    def __init__(self, shifts, num_nurses, num_days, num_shifts, limit):
        cp_model.CpSolverSolutionCallback.__init__(self)
        self._shifts = shifts
        self._num_nurses = num_nurses
        self._num_days = num_days
        self._num_shifts = num_shifts
        self._solution_count = 0
        self._solution_limit = limit

    def on_solution_callback(self):
        self._solution_count += 1
        print('Solution %i' % self._solution_count)
        for d in range(self._num_days):
            print('Day %i' % d)
            for n in range(self._num_nurses):
                is_working = False
                for s in range(self._num_shifts):
                    if self.Value(self._shifts[(n, d, s)]):
                        is_working = True
                        print('  Nurse %i works shift %i' % (n, s))
                if not is_working:
                    print('  Nurse {} does not work'.format(n))
        if self._solution_count >= self._solution_limit:
            print('Stop search after %i solutions' % self._solution_limit)
            self.StopSearch()

    def solution_count(self):
        return self._solution_count

# Display the first five solutions.
solution_limit = 5
solution_printer = NursesPartialSolutionPrinter(shifts, num_nurses,
                                                num_days, num_shifts,
                                                solution_limit)
#调用求解器,并显示前五个解决方案
solver.Solve(model, solution_printer)

output :
insert image description here

2-2. Job shop scheduling problem

Job Shop Scheduling Problem: Each job consists of a sequence of tasks that must be executed in a given order, and each task must be processed on a specific machine. For example, the position could be a manufacturer of a single consumer product such as an automobile. The problem is, to schedule tasks on machines so as to minimize the length of the schedule, i.e. the time required to complete all jobs.

This problem has the following constraints:

  • No tasks for this job can be started until the previous job task has completed.
  • A machine can only handle one task at a time.
  • Must run to complete after the task has started.

The following is an example of a simple job shop scheduling problem, where each task is labeled by a pair of numbers (m, p), where m is the number of machines on which the task must be processed, and p is the processing time of the task (i.e. takes time). (Job and machine numbers start at 0.)

  • Job 0 = [(0, 3), (1, 2), (2, 2)]
  • Job 1 = [(0, 2), (2, 1), (1, 4)]
  • Job 2 = [(1, 4), (2, 3)]

In the example, job 0 has 3 tasks. The first (0, 3) must be processed on machine 0 in 3 units of time. The second (1, 2) must be processed on machine 1 in 2 units of time, and so on. There are eight missions in total.

Below is a possible solution :
insert image description here
Problem variables and constraints
This section describes how to set up the problem variables and constraints. First, let task(i, j) denote the jth task in job i. For example, task(0, 2) represents the second task of job 0, corresponding to the pair (1, 2) in the problem statement.

Next, ti , j t_{i, j}ti,jDefined as the start time of task(i, j). ti , j t_{i, j}ti,jis the variable in solving the homework problem. The solution requires determining the values ​​of these variables that satisfy the requirements of the problem.

The job shop scheduling problem has two constraints :

  • Priority Constraint - A condition derived from a job that, for any two consecutive tasks in the same job, the first task must complete before the second can start. For example, task(0, 2) and task(0, 3) are consecutive tasks for job 0. Since task(0, 2) has a processing time of 2, the start time of task(0, 3) must be at least 2 units later than task 2's start time. (Task 2 might be painting a door, which takes 2 hours to paint.) Thus, you are bound by:
    t 0 , 2 t_{0, 2}t0,2 + 2 <= t 0 , 3 t_{0, 3} t0,3
  • No Overlap Constraint - There is a restriction that the machine cannot handle two tasks at the same time. For example, tasks (0, 2) and tasks (2, 1) are both processed on machine 1. Since the processing times are 2 and 4 respectively, one of the following constraints must be satisfied:
    t 0 , 2 t_{0, 2}t0,2 + 2 <= t 2 , 1 t_{2, 1} t2,1(if task(0, 2) is scheduled before task(2, 1)) or
    t 2 , 1 t_{2, 1}t2,1 + 4 <= t 0 , 2 t_{0, 2} t0,2(if task(2, 1) is scheduled before task(0, 2)).

Problem Objective
The objective of the job shop scheduling problem is to minimize the makespan: the length of time from the earliest start time of the job to the latest finish time of the job.

The code looks like this :

"""Minimal jobshop example."""
import collections
from ortools.sat.python import cp_model


def main():
    """Minimal jobshop problem."""
    # Data.
    # # 定义每一个作业的所有任务,(m,p)m代表的是在哪个机器上处理,p代表的是任务的处理时间(即所需要的时间)
    jobs_data = [  # task = (machine_id, processing_time).
        [(0, 3), (1, 2), (2, 2)],  # Job0
        [(0, 2), (2, 1), (1, 4)],  # Job1
        [(1, 4), (2, 3)]  # Job2
    ]
    # 机器数量,得出一共需要的机器数量是多少。
    machines_count = 1 + max(task[0] for job in jobs_data for task in job)
    all_machines = range(machines_count)
    # Computes horizon dynamically as the sum of all durations.
    # 处理所有任务的总的时长并实例化模型
    horizon = sum(task[1] for job in jobs_data for task in job)

    # Create the model.
    model = cp_model.CpModel()
    
    # 使用collection来命名元组,详细使用看下边内容
    # Named tuple to store information about created variables.
    task_type = collections.namedtuple('task_type', 'start end interval')
    # Named tuple to manipulate solution information.
    assigned_task_type = collections.namedtuple('assigned_task_type',
                                                'start job index duration')

    # Creates job intervals and add to the corresponding machine lists.
    all_tasks = {
    
    }
    # 使用collections来创建字典。
    machine_to_intervals = collections.defaultdict(list)
    
    # job_id:第几个作业, job: [(0, 3), (1, 2), (2, 2)]
    for job_id, job in enumerate(jobs_data):
        # task_id: 第几个任务, task: (0, 3)
        for task_id, task in enumerate(job):
            machine = task[0]
            duration = task[1]
            # 拼接后缀
            suffix = '_%i_%i' % (job_id, task_id)
            start_var = model.NewIntVar(0, horizon, 'start' + suffix)
            end_var = model.NewIntVar(0, horizon, 'end' + suffix)
            # 创建时间区间变量来存储每个任务
            interval_var = model.NewIntervalVar(start_var, duration, end_var,
                                                'interval' + suffix)
            
            # 
            all_tasks[job_id, task_id] = task_type(start=start_var,
                                                   end=end_var,
                                                   interval=interval_var)
            # 在机器上添加每个时间区间变量
            machine_to_intervals[machine].append(interval_var)

    # Create and add disjunctive constraints.
    # 创建每个机器的限制
    # # 添加一个“无重叠”约束的方法,通常用于安排一组任务或者活动在时间上不会发生重叠,这可以保证在特定时间段内,只有一个任务或活动会被安排。
    for machine in all_machines:
        model.AddNoOverlap(machine_to_intervals[machine])

    # Precedences inside a job.
    # 添加限制条件:后一个任务的起始时间大于等于前一个任务的结束时间
    for job_id, job in enumerate(jobs_data):
        for task_id in range(len(job) - 1):
            model.Add(all_tasks[job_id, task_id +
                                1].start >= all_tasks[job_id, task_id].end)

    # Makespan objective.
    # 目标变量:所有作业的最后一个任务的结束时间,作为目标变量的最大值约束
    obj_var = model.NewIntVar(0, horizon, 'makespan')
    model.AddMaxEquality(obj_var, [
        all_tasks[job_id, len(job) - 1].end
        for job_id, job in enumerate(jobs_data)
    ])
    
    # 最小化目标变量
    model.Minimize(obj_var)

    # Creates the solver and solve.
    solver = cp_model.CpSolver()
    status = solver.Solve(model)
    
    # 
    if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
        print('Solution:')
        # 创建每个机器被分配的任务列表
        # Create one list of assigned tasks per machine.
        assigned_jobs = collections.defaultdict(list)
        for job_id, job in enumerate(jobs_data):
            for task_id, task in enumerate(job):
                machine = task[0]
                assigned_jobs[machine].append(
                    assigned_task_type(start=solver.Value(
                        all_tasks[job_id, task_id].start),
                                       job=job_id,
                                       index=task_id,
                                       duration=task[1]))

        # Create per machine output lines.
        output = ''
        for machine in all_machines:
            # Sort by starting time.
            assigned_jobs[machine].sort()
            sol_line_tasks = 'Machine ' + str(machine) + ': '
            sol_line = '           '

            for assigned_task in assigned_jobs[machine]:
                name = 'job_%i_task_%i' % (assigned_task.job,
                                           assigned_task.index)
                # Add spaces to output to align columns.
                sol_line_tasks += '%-15s' % name

                start = assigned_task.start
                duration = assigned_task.duration
                sol_tmp = '[%i,%i]' % (start, start + duration)
                # Add spaces to output to align columns.
                sol_line += '%-15s' % sol_tmp

            sol_line += '\n'
            sol_line_tasks += '\n'
            output += sol_line_tasks
            output += sol_line

        # Finally print the solution found.
        print(f'Optimal Schedule Length: {
    
    solver.ObjectiveValue()}')
        print(output)
    else:
        print('No solution found.')

    # Statistics.
    print('\nStatistics')
    print('  - conflicts: %i' % solver.NumConflicts())
    print('  - branches : %i' % solver.NumBranches())
    print('  - wall time: %f s' % solver.WallTime())


if __name__ == '__main__':
    main()

output :

*Solution:
Optimal Schedule Length: 11.0
Machine 0: job_1_task_0 job_0_task_0
[0,2] [2,5]
Machine 1: job_2_task_0 job_0_task_1 job_1_task_2
[0,4] [5,7] [7,11]
Machine 2: job_1_task_1 job_2_task_1 job_0_task_2
[2,3] [4,7] [7,9]
Statistics

  • conflicts: 0
  • branches : 0
  • wall time: 0.018851 s*

3. Knowledge base

3-1、collection

In Python, collections is a standard library module that provides some useful data structures and tools for extending the functionality of built-in data types. The collections module contains various collection types that can be used to manage and process data more efficiently. Some commonly used collection types include :

  • namedtuple (named tuple) : namedtuple is a factory function used to create a named tuple subclass. Each element of a named tuple has its own field name, which allows each position of the tuple to have a human-readable name. It can make code more readable.
  • deque (two-way queue) : deque is a two-way queue that supports efficient insertion and deletion operations at both ends of the queue. Compared to a list, the time complexity of inserting and removing elements at the head of a deque is O(1), while the time complexity of a list is O(n).
  • Counter : Counter is a simple counter utility for counting occurrences of hashable objects. It can be used to count the frequency of elements in lists, strings, etc.
  • OrderedDict (ordered dictionary) : OrderedDict is an ordered dictionary type that maintains the order in which elements are inserted, and the dictionary can be traversed in the order of insertion.
  • defaultdict (default dictionary) : defaultdict is a dictionary type that can specify a default value. When accessing a key that does not exist in the dictionary, the specified default value will be returned instead of throwing an exception.
  • ChainMap (chain mapping) : ChainMap can link multiple dictionaries or mapping objects together to form a logical mapping chain.
  • Counter : Counter is a simple counter utility for counting occurrences of hashable objects. It can be used to count the frequency of elements in lists, strings, etc.

The following takes namedtuple (named tuple as an example) : a factory function to create a named tuple subclass, and generate a tuple subclass that can use names to access element content
Parameter introduction
namedtuple(typename,field_names,*,verbose=False, rename=False, module=None)

  • typename: This parameter specifies the class name of the created tuple subclass, which is equivalent to the user defining a new class.

  • field_names: This parameter is a sequence of strings, such as ['x', 'y']. In addition, field_names can also directly use a single string to represent all field names, and multiple field names are separated by spaces or commas, such as 'x y' or 'x,y'. Any valid Python identifier can be used as a field name (no leading underscore). Valid identifiers can consist of letters, numbers, and underscores, but cannot start with numbers, underscores, or keywords (such as return, global, pass, raise, etc.).

  • rename: If this parameter is set to True, invalid field names will be automatically replaced with positional names. For example, if you specify ['abc', 'def', 'ghi', 'abc'], it will be replaced by ['abc', '_1', 'ghi', '_3'], because the def field name is a keyword, and the abc field name is repeated.

  • verbose: If this parameter is set to True, then when the subclass is created, the class definition is printed out immediately.

  • module: If this parameter is set, the class will be located under this module, so the module attribute of this custom class will be set to this parameter value.

Case study :

from collections import namedtuple

# 定义命名元组类型
task_type = namedtuple('task_type', 'start end interval')

# 创建一个命名元组实例
start_var = 0
end_var = 10
interval_var = 5
task_instance = task_type(start=start_var, end=end_var, interval=interval_var)

# 访问命名元组的字段值
print(task_instance.start)    # 输出: 0
print(task_instance.end)      # 输出: 10
print(task_instance.interval) # 输出: 5

# 定义了一个命名元组 task_type,它有三个字段:start、end 和 interval。
# 接下来,task_type(start=start_var, end=end_var, interval=interval_var) 这行代码实际上是在创建一个 task_type 命名元组的实例,并将参数 start_var、end_var 和 interval_var 分别赋值给相应的字段。
# 这样的命名元组实例可以像普通的元组一样被访问和操作,但是由于每个字段都有一个名字,所以可以通过字段名来获取元组的值,使得代码更加清晰易读

3-2、CpModel().AddNoOverlap()

Meaning : It is the method used to add a "no overlap" constraint. A "no overlap" constraint is often used to schedule a set of tasks or activities so that they do not overlap in time. This ensures that only one task or activity is scheduled during a specific time period.

Case study :

from ortools.sat.python import cp_model
model = cp_model.CpModel()
# 任务1,开始时间为0,持续时间为2,结束时间为2
start_1 = model.NewIntVar(0, 10, 'start_1')
duration_1 = 2
end_1 = model.NewIntVar(0, 10, 'end_1')
interval_1 = model.NewIntervalVar(start_1, duration_1, end_1, 'interval_1')

# 任务2,开始时间为0,持续时间为2,结束时间为2
start_2 = model.NewIntVar(0, 10, 'start_2')
duration_2 = 2
end_2 = model.NewIntVar(0, 10, 'end_2')
interval_2 = model.NewIntervalVar(start_2, duration_2, end_2, 'interval_2')

# 任务3,开始时间为0,持续时间为2,结束时间为2
start_3 = model.NewIntVar(0, 10, 'start_3')
duration_3 = 2
end_3 = model.NewIntVar(0, 10, 'end_3')
interval_3 = model.NewIntervalVar(start_3, duration_3, end_3, 'interval_3')
model.AddNoOverlap([interval_1, interval_2, interval_3])
solver = cp_model.CpSolver()
status = solver.Solve(model)

if status == cp_model.OPTIMAL:
    print('任务1开始于:', solver.Value(start_1))
    print('任务2开始于:', solver.Value(start_2))
    print('任务3开始于:', solver.Value(start_3))

output :
insert image description here

3-3、CpModel().AddMaxEquality()

Meaning : model.AddMaxEquality() is a method provided in the Constraint Programming module (cp_model), which is used to add a constraint that the maximum value of a set of variables is equal to the model.

grammar

model.AddMaxEquality(target_var, variable_array)
  • target_var : Indicates the target variable, which is an integer variable (IntVar) used to store the maximum value in the variable array.
  • variable_array : Indicates a variable array, that is, a set of integer variables that need to find the maximum value.
  • The AddMaxEquality () method will set the value of the target variable target_var to the maximum value in the variable array variable_array.

The case is as follows :

# Import the required module
from ortools.sat.python import cp_model

# Instantiate the CP-SAT model.
model = cp_model.CpModel()

# Define the capacities
br1_cap = 5
br2_cap = 4
br3_cap = 6

# Given tasks
tasks = 10

# Define variables for the tasks assigned to each branch
br1_tasks = model.NewIntVar(0, br1_cap, 'br1')
br2_tasks = model.NewIntVar(0, br2_cap, 'br2')
br3_tasks = model.NewIntVar(0, br3_cap, 'br3')

# Define the variable for the maximum tasks
max_tasks = model.NewIntVar(0, max(br1_cap, br2_cap, br3_cap), 'max_tasks')

# Tell the model that max_tasks is the maximum number of tasks assigned to any branch
model.AddMaxEquality(max_tasks, [br1_tasks, br2_tasks, br3_tasks])

# Add constraints that the tasks assigned to all branches should equal the given tasks
model.Add(br1_tasks + br2_tasks + br3_tasks == tasks)

# Create a solver
solver = cp_model.CpSolver()
status = solver.Solve(model)

# Print the assignments
if status == cp_model.OPTIMAL:
    print("Branch 1 tasks: ", solver.Value(br1_tasks))
    print("Branch 2 tasks: ", solver.Value(br2_tasks))
    print("Branch 3 tasks: ", solver.Value(br3_tasks))
    print("Maximum tasks assigned to a branch: ", solver.Value(max_tasks))

输出如下
Branch 1 tasks: 0
Branch 2 tasks: 4
Branch 3 tasks: 6
Maximum tasks assigned to a branch: 6

3-4、cp_model.CpModel().NewIntVar()

Meaning : cp_model.CpModel().NewIntVar() is a method for creating a new integer variable (IntVar). Integer variables are used to model integer-type unknowns that can represent states, decision variables, or other values ​​that need to be solved in a problem.
Grammar :

NewIntVar(lb, ub, name)

Parameter description :

  • lb (lower bound): The lower bound of the integer variable, indicating the minimum value range of the integer variable.
  • ub (upper bound): The upper bound of the integer variable, indicating the maximum value of the value range of the integer variable.
  • name: The name of the integer variable (optional), used to identify and identify the variable in the output.

Case study :

from ortools.sat.python import cp_model

def simple_integer_variable_example():
    # 创建约束编程模型
    model = cp_model.CpModel()

    # 创建整数变量 x,范围为 [0, 10]
    x = model.NewIntVar(0, 10, 'x')

    # 创建约束条件:x >= 5
    model.Add(x >= 5)

    # 创建求解器并求解模型
    solver = cp_model.CpSolver()
    status = solver.Solve(model)

    if status == cp_model.OPTIMAL:
        print('x 的值:', solver.Value(x))
    else:
        print('找不到可行解。')

if __name__ == '__main__':
    simple_integer_variable_example()

Value of output :
Value of x: 5

3-5、cp_model.CpModel().NewIntervalVar

Meaning : cp_model.CpModel().NewIntervalVar() is a method for creating a time interval variable (IntervalVar). Time interval variables are used to represent the beginning and end of a task or activity in time.

The syntax of the NewIntervalVar() method is as follows :

NewIntervalVar(start_var, duration, end_var, name)

Parameter description :

  • start_var: Integer variable (IntVar) or integer value representing the start time of the task. Usually an integer variable, but can also be a fixed integer value representing the start time of the task.
  • duration: An integer value or integer variable representing the duration of the task. The task duration can be a fixed integer value or an integer variable whose value is determined at runtime.
  • end_var: Integer variable (IntVar) or integer value representing the end time of the task. Usually an integer variable, but can also be a fixed integer value representing the end time of the task.
  • name: The name of the time interval variable (optional), used to identify and identify the variable in the output.

The characteristic of time interval variables is that they allow the start and end times of tasks to be flexibly adjusted at solution time to satisfy given constraints such as non-overlap between tasks, duration of tasks, etc.

Case study :

from ortools.sat.python import cp_model

def simple_interval_variable_example():
    # 创建约束编程模型
    model = cp_model.CpModel()

    # 创建整数变量表示任务的开始时间和结束时间
    start_var = model.NewIntVar(0, 100, 'start_var')
    end_var = model.NewIntVar(0, 100, 'end_var')

    # 创建整数变量表示任务的持续时间
    duration = model.NewIntVar(10, 50, 'duration')

    # 创建时间区间变量
    task_interval = model.NewIntervalVar(start_var, duration, end_var, 'task_interval')

    # 创建约束条件:任务的开始时间和结束时间之间存在关系
    model.Add(end_var == start_var + duration)

    # 创建求解器并求解模型
    solver = cp_model.CpSolver()
    status = solver.Solve(model)

    if status == cp_model.OPTIMAL:
        print('任务开始时间:', solver.Value(start_var))
        print('任务结束时间:', solver.Value(end_var))
        print('任务持续时间:', solver.Value(duration))
    else:
        print('找不到可行解。')

if __name__ == '__main__':
    simple_interval_variable_example()

output :

Task start time: 0
Task end time: 10
Task duration: 10

Reference article:
OR-Tools official website .
[Explanation of 10,000-word long text] Python library collections, let you beat 99% of Pythoner .


Summarize

How many times do you have to experience it before you step into the same river over and over again.

Guess you like

Origin blog.csdn.net/weixin_42475060/article/details/131959306