【计算智能算法--遗传算法解决调度问题】

采用遗传算法解决调度问题代码部分

问题描述

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值