问题描述
1.忽略三条生产线之间的差异,三条生产线的生产能力完全相同;
2.每四个小时换一次产品,原材料无限多;
3.假设工人的工资按4个小时进行结算,且越偏离正常工作时间工资越高;
4.不考虑资源、管理的费用,默认所有的费用都包含在生产费用里面;
5.忽略工人的差异性,所有的工人都有相同的生产能力;
6.没能按照合同完成的订单,需要交纳其订单总金额的10%罚款;
7.每天早上8点进行订生产计划的安排,当订单在早上8点之后的订单放在第二天统一安排;
8.忽略生产过程中的一切意外情况,所有设备、人员均能正常工作;
9.假设三条生产线都只能生产三种产品 ;
10.假设合同的截止时间均是指截止日期的当天早上8点;
11.当多个订单同时加工且订单中存在最后一部分工件的加工时间小于4小时,可以考虑将同一类产品多余的部分合在一起加工。
建模
解的形式是在3条生产线上的生产顺序,解的长度为起始时间到最大截止时间(或者规定也可以,如下图所示)
订单合并以列表方式在解中体现
结果展示
代码部分
from datetime import datetime, timedelta
import random
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from concurrent.futures import ThreadPoolExecutor, as_completed
"""工厂信息"""
# 生产一单位特定数量产品获利
product_profits = {"A": 20, "B": 30, "C": 40}
# 生产特定数量产品所需的时间,"A": 4 / 100 表示生产产品类型 A 的每100单位需要4小时。
production_time_per_unit = {"A": 4 / 100, "B": 4 / 100, "C": 4 / 100}
# 工人薪资 正常工作时间和非正常工作时间
worker_salary = [20,40]
# 正常工作时间段
normal_working_hours = (8, 18)
"""## 订单类"""
class Order:
def __init__(self, id, product_type, quantity, arrival_time, deadline, penalty_ratio):
self.id = id
self.product_type = product_type # 产品类型
self.quantity = quantity # 订单数量
self.arrival_time_real = arrival_time # 订单到达时间
self.deadline = deadline # 截止时间
# self.penalty_ratio = penalty_ratio
self.penalty_amount = penalty_ratio * self.quantity*product_profits[self.product_type]
self.completed_quantity = 0 # 订单已完成的数量
self.penalty_calculated=False
self.completed_time = None # 订单完成时间
self.adjust_order_arrival_time()
def calculate_penalty(self, delivery_time=None):
"""订单超时惩罚金计算。如果订单超时,承受10%违约金"""
delivery_time = self.completed_time if delivery_time is None else delivery_time
if self.penalty_calculated :
return 0
else:
if delivery_time > self.deadline:
return self.penalty_amount
self.penalty_calculated=True
return 0
def adjust_order_arrival_time(self):
"""调整订单到达时间。如果订单晚于当天早上8点到达,将其设置为第二天早上8点。"""
if self.arrival_time_real.hour > 8 \
or (self.arrival_time_real.hour == 8 and self.arrival_time_real.minute > 0):
# 设置订单到达时间为第二天早上8点
next_day = self.arrival_time_real.date() + timedelta(days=1)
self.arrival_time = datetime(next_day.year, next_day.month, next_day.day, 8, 0, 0)
else:
self.arrival_time = self.arrival_time_real
def reset(self):
self.completed_quantity = 0
self.completed_time = None
"""## 适应度计算"""
def calculate_order_profit(order, product_profits):
"""计算订单的利润。"""
return product_profits[order.product_type] * order.completed_quantity
def calculate_wage_cost(current_time, normal_working_hours, worker_salary):
"""计算工资成本。"""
if current_time.hour < normal_working_hours[0] or current_time.hour >= normal_working_hours[1]:
return worker_salary[1] # 非正常工作时间
else:
return worker_salary[0] # 正常工作时间
def process_order(current_time, order_id, production_time_per_unit,orders):
if type(order_id) is list:
order = [orders[o] for o in order_id]
produced_quantity = 0
for o in order:
quantity = o.quantity - o.completed_quantity
produced_quantity += quantity
o.completed_quantity += produced_quantity
# 订单已完成
if o.completed_time is None:
o.completed_time = current_time + timedelta(hours=4)
else:
order = orders[order_id]
if current_time < order.arrival_time:
return
production_capacity = 4 / production_time_per_unit[order.product_type]
produced_quantity = min(order.quantity - order.completed_quantity, production_capacity)
order.completed_quantity += produced_quantity
# 若订单已完成
if order.completed_quantity == order.quantity:
if order.completed_time is None:
order.completed_time = current_time + timedelta(hours=4)
def fitness_function(chromosome, orders, start_time, product_profits, normal_working_hours,end_time=None):
"""适应度函数,总利润=总收益-总惩罚-总工资"""
total_profit = 0
total_penalty = 0
total_wage_cost = 0
orders_reset(orders)
transposed_schedule = transpose_schedule(chromosome)
current_time = start_time
for order_ids_in_time in transposed_schedule:
for order_id in order_ids_in_time:
if order_id:
process_order(current_time, order_id, production_time_per_unit,orders)
total_wage_cost += calculate_wage_cost(current_time, normal_working_hours, worker_salary)
current_time += timedelta(hours=4)
# 计算总收益和总惩罚
for order in orders.values():
total_profit += calculate_order_profit(order,product_profits)
if order.completed_time is None:
total_penalty += order.calculate_penalty(end_time)
else:
total_penalty += order.calculate_penalty()
return total_profit - total_penalty - total_wage_cost
"""## 解的处理"""
def get_remain_orders(schedule, orders, start_work_time, product_profits, normal_working_hours,end_time):
"""剩余量的计算"""
fitness_function(schedule, orders, start_work_time, product_profits, normal_working_hours,end_time)
remain_orders = {}
for id,order in orders.items():
if order.quantity > order.completed_quantity:
order.quantity-=order.completed_quantity
order.arrival_time=end_time
order.reset()
remain_orders[id] = order
return remain_orders
def orders_reset(orders):
"""重置订单计算信息"""
for order in orders.values():
order.reset()
def generate_initial_population(population_size, num_time_slots, orders, start_time ):
population = []
order_ids=list(orders.keys())
for _ in range(population_size):
chromosome = [[random.choice(order_ids + [None]) for _ in range(num_time_slots)] for _ in range(3)]
chromosome = repair_solution(chromosome, orders, start_time, production_time_per_unit)
population.append(chromosome)
return population
def plot_schedule(chromosome_transposed, orders):
fig, ax = plt.subplots(figsize=(10, 6))
# 定义颜色
colors = list(mcolors.TABLEAU_COLORS.keys())
order_colors = {order_id: colors[i % len(colors)] for i, order_id in enumerate(orders)}
# 遍历转置后的解
for time_slot, production_line in enumerate(chromosome_transposed):
for line_number, order_id in enumerate(production_line):
if order_id:
if type(order_id) is list:
total_time=0
for o in order_id:
order = orders[o]
order_time_in_slot = (order.quantity*production_time_per_unit[order.product_type])%4
# 绘制每个订单的生产块
ax.broken_barh([(time_slot * 4+total_time, order_time_in_slot)], (line_number * 1.5, 1),
facecolors=order_colors[o],
edgecolor='black')
total_time += order_time_in_slot
# 绘制合并生产块的空白部分
ax.broken_barh([(time_slot * 4 + total_time, 4-total_time)], (line_number * 1.5, 1),
facecolors='white',
edgecolor=None)
else:
# 绘制每个订单的生产块
ax.broken_barh([(time_slot * 4, 4)], (line_number * 1.5, 1),
facecolors=order_colors[order_id],
edgecolor='black')
# 设置图表格式
ax.set_xlabel('Time (hours)')
ax.set_ylabel('Production Line')
ax.set_yticks([1.5 * i + 0.5 for i in range(len(chromosome_transposed[0]))])
ax.set_yticklabels([f"Line {i+1}" for i in range(len(chromosome_transposed[0]))])
ax.set_xticks([i * 4 for i in range(len(chromosome_transposed) + 1)])
ax.set_title('Manufacturing Schedule')
ax.grid(True)
# 创建图例
legend_elements = [plt.Line2D([0], [0], color=order_colors[order_id], lw=4, label=order_id) for order_id in orders]
ax.legend(handles=legend_elements, loc='upper left')
plt.show()
def transpose_schedule(chromosome):
"""转置解,即将每条生产线上的订单排列转换为每个时间段的生产线排列。"""
return list(map(list, zip(*chromosome)))
"""## 解的修复"""
def merge_order(order_id,order,production_capacity,orders):
"""合并同一类产品的多余部分进行加工。"""
merge_orders_ids = [order_id]
order_capacity = order.quantity - order.completed_quantity
order.completed_quantity += order_capacity
remain_capacity = production_capacity - order_capacity
for other_order_id, other_order in orders.items():
if (other_order.id != order.id) \
and (other_order.product_type == order.product_type):
order_capacity = other_order.quantity - other_order.completed_quantity
if order_capacity <= remain_capacity and order_capacity != 0:
remain_capacity -= order_capacity
other_order.completed_quantity += order_capacity
merge_orders_ids.append(other_order_id)
return merge_orders_ids
def optimize_solution(chromosome, orders, start_time):
"""优化解:对于部分空位置,若后续工序可以进行,将后续工序提前。"""
for line in chromosome:
for i in range(len(line) - 1):
if line[i] is None:
# 查找后续可以提前的订单
for j in range(i + 1, len(line)):
next_order_id = line[j]
if type(next_order_id) is not list and next_order_id \
and orders[next_order_id].arrival_time <= start_time + timedelta(hours=4*i):
# 提前后续订单
line[i], line[j] = line[j], None
break
return chromosome
def repair_solution(chromosome, orders, start_time, production_time_per_unit):
""" 修复解:确保所有订单都在到来时间之后开始处理,并考虑订单的完成量 """
orders_reset(orders)
chromosome=optimize_solution(chromosome, orders, start_time)
transposed_schedule = transpose_schedule(chromosome)
current_time = start_time
for orders_in_slot in transposed_schedule:
for i,order_id in enumerate(orders_in_slot):
if order_id:
if type(order_id) is list:
order_id = order_id[0]
order = orders[order_id]
if current_time < order.arrival_time or order.completed_quantity >= order.quantity:
orders_in_slot[i] = None # 移除不可行或已完成的订单
else:
# 更新订单完成量
production_capacity = 4 / production_time_per_unit[order.product_type]
if (order.quantity - order.completed_quantity) > production_capacity:
order.completed_quantity += min(order.quantity - order.completed_quantity, production_capacity)
else:
"""合并同一类产品的多余部分进行加工。"""
orders_in_slot[i] = merge_order(order_id,order,production_capacity,orders)
current_time += timedelta(hours=4)
chromosome = transpose_schedule(transposed_schedule)
chromosome = optimize_solution(chromosome, orders, start_time)
orders_reset(orders)
return chromosome
""""## 遗传算法"""
def crossover(parent1, parent2):
child1 = [line[:] for line in parent1]
child2 = [line[:] for line in parent2]
crossover_point = random.randint(1, len(child1[0]) - 1)
for i in range(len(child1)):
child1[i][crossover_point], child2[i][crossover_point] = child2[i][crossover_point], child1[i][crossover_point]
return child1, child2
def mutate(chromosome, mutation_rate, orders, start_time):
for line in chromosome:
for i in range(len(line)):
if random.random() < mutation_rate:
line[i] = random.choice(list(orders.keys()) + [None])
chromosome = repair_solution(chromosome, orders, start_time, production_time_per_unit)
return chromosome
def genetic_algorithm(orders, population, generations, mutation_rate, fitness_function, crossover_function, mutation_function,
production_time_per_unit, start_time, end_time, ismutil = True, isdraw=False):
best_fitness = float('-inf')
best_schedule = None
best_fitnesses = []
generation_best_fitnesses=[]
for _ in range(generations):
if ismutil:
# 使用线程池执行适应度计算
with ThreadPoolExecutor(max_workers=16) as executor:
future_to_fitness = {executor.submit(fitness_function, chromosome, orders.copy(), start_time, product_profits,
normal_working_hours, end_time): chromosome for chromosome in population}
population = []
fitness_values = []
for future in as_completed(future_to_fitness):
fitness_values.append(future.result())
population.append(future_to_fitness[future])
else:
fitness_values = [fitness_function(chromosome, orders, start_time, product_profits, normal_working_hours,
end_time=end_time) for chromosome in population]
sorted_population = [x for _, x in sorted(zip(fitness_values, population), key=lambda pair: pair[0], reverse=True)]
fitness_values = sorted(fitness_values,reverse=True)
population = sorted_population[:int(0.5 * len(population))]
while len(population) < len(sorted_population):
parent1, parent2 = random.sample(population, 2)
child1, child2 = crossover_function(parent1, parent2)
population.extend([mutation_function(child1, mutation_rate, orders, start_time),
mutation_function(child2, mutation_rate, orders, start_time)])
if fitness_values[0] > best_fitness:
best_fitness = fitness_values[0]
best_schedule = sorted_population[0]
if isdraw:
best_fitnesses.append(best_fitness)
generation_best_fitnesses.append(fitness_values[0])
if isdraw: return best_fitnesses,generation_best_fitnesses
return best_schedule
"""采用遗传算法求解"""
def run_main(orders,start_work_time,end_time):
# 遗传算法参数
population_size = 50
generations = 400
mutation_rate = 0.1
# 计算时间段的数量
num_days = (end_time - start_work_time).days + 1 # 包括最后一天
num_time_slots = num_days * 6 # 假设每天24小时工作时间,每4小时更换产品
# 运行遗传算法
initial_population = generate_initial_population(population_size, num_time_slots, orders,start_work_time)
best_schedule = genetic_algorithm(orders, initial_population, generations, mutation_rate, fitness_function,
crossover, mutate, production_time_per_unit, start_work_time, end_time)
print(transpose_schedule(best_schedule))
print(fitness_function(best_schedule, orders, start_work_time, product_profits, normal_working_hours,
end_time))
remain_orders = get_remain_orders(best_schedule, orders, start_work_time, product_profits, normal_working_hours,
end_time)
# 绘制订单加工工序图
plot_schedule(transpose_schedule(best_schedule), orders)
return remain_orders
"""绘制遗传算法参数变动对模型的影响"""
def draw_GA(orders, start_work_time, end_time):
# 遗传算法参数
population_size = 50
generations = 400
mutation_rate = 0.1
# 计算时间段的数量
num_days = (end_time - start_work_time).days + 1 # 包括最后一天
num_time_slots = num_days * 6 # 假设每天24小时工作时间,每4小时更换产品
# 绘图初始化
colors = list(mcolors.CSS4_COLORS.keys())
plt.figure(figsize=(12, 6))
for i, population_size in enumerate(range(10, 71, 20)):
# 运行遗传算法
initial_population = generate_initial_population(population_size, num_time_slots, orders,start_work_time)
for j in range(1,5,1):
mutation_rate=j/10
best_fitnesses,generation_best_fitnesses = genetic_algorithm(orders, initial_population, generations, mutation_rate, fitness_function,
crossover, mutate, production_time_per_unit, start_work_time, end_time,isdraw=True)
# 绘制每一代的最优适应度
plt.plot(best_fitnesses, label=f'population_size:{population_size},mutation_rate:{mutation_rate}',
# f',Best Fitness Over All Generations',
color=colors[i*4+j])
# 绘制每一代的代际最优适应度
# plt.plot(generation_best_fitnesses, label=f'population_size:{population_size},mutation_rate:{mutation_rate},Generation Best Fitness',
# color=colors[-j], linestyle='dashed')
plt.xlabel('Generation')
plt.ylabel('Fitness')
plt.title('Fitness Trends in Genetic Algorithm')
plt.legend()
plt.grid(True)
plt.show()
"""测试订单产生"""
def generate_random_orders(num_orders, product_types, start_date, end_date, penalty_ratio):
random_orders = {}
for i in range(1, num_orders + 1):
product_type = random.choice(product_types)
quantity = random.randint(100, 2000)
arrival_time = random_date(start_date, end_date)
deadline = random_date(arrival_time, end_date,isddl=True)
order_id = f"O{str(i).zfill(3)}"
order = Order(order_id, product_type, quantity, arrival_time, deadline, penalty_ratio)
random_orders[order_id] = order
return random_orders
def random_date(start, end,isddl=False):
delta = end - start
if delta.days <= 0:
# 如果时间范围不足一天,则直接返回结束时间
return end
random_days = random.randrange(delta.days)
random_time = timedelta(hours=8, minutes=0)if isddl else timedelta(hours=random.randrange(24), minutes=random.randrange(60))
return start + timedelta(days=random_days) + random_time
if __name__ == "__main__":
# # 示例订单
# orders = {
# "O001": Order("O001", "A", 10000, datetime(2024, 1, 4, 8, 0, 0), datetime(2024, 1, 6, 8, 0, 0), 0.1),
# "O002": Order("O002", "B", 1500, datetime(2024, 1, 4, 12, 0, 0), datetime(2024, 1, 7, 8, 0, 0), 0.1),
# "O003": Order("O003", "C", 2000, datetime(2024, 1, 5, 8, 0, 0), datetime(2024, 1, 8, 8, 0, 0), 0.1)
# }
#
# start_work_time = datetime(2024, 1, 4, 8, 0, 0)
# # # 计算最大截止时间
# latest_deadline = max(order.deadline for order in orders.values())
# remain_orders = run_main(orders, start_work_time=start_work_time, end_time=latest_deadline)
"""随机获取新订单"""
num_new_orders = 20
product_types = ["A", "B", "C"]
start_date = datetime(2024, 1, 4, 8, 0, 0)
end_date = datetime(2024, 1, 15, 8, 0, 0)
penalty_ratio = 0.1
new_orders = generate_random_orders(num_new_orders, product_types, start_date, end_date, penalty_ratio)
# new_orders.update(remain_orders)
# run_main(new_orders,start_date,end_date)
draw_GA(new_orders, start_date, end_date)