同系列算法问题

回溯法解决N皇后问题-Python实现(全排列+剪枝)

贪心算法解决活动安排-Python实现(排序+贪心选择)


问题

有n个作业(编号为1~n)要在由两台机器M1和M2组成的流水线上完成加工。每个作业加工的顺序都是先在M1上加工,然后在M2上加工。M1和M2加工作业i所需的时间分别为ai和bi(1≤i≤n)。

流水作业调度问题要求确定这n个作业的最优加工顺序,使得从第一个作业在机器M1上开始加工,到最后一个作业在机器M2上加工完成所需的时间最少。可以假定任何作业一旦开始加工,就不允许被中断,直到该作业被完成,即非优先调度。


问题概述

背景条件:两台机器,分别为机器1和机器2,多个作业,每个作业必须在机器1完成的基础上,才能在机器2加工。根据机器和作业分别有以下结论:

结论1:针对机器,在作业未完成的条件下,机器1的状态是连续工作,但机器2的状态可以是空闲和工作;

结论2:针对作业,n个作业,则有n!种排列可能;

结论3:作业的加工时间,是最后一个作业在机器2上的完成时间。


分析问题

根据问题概述所得的3个结论,有以下需要实现:

1.需要获得每个作业的在2台机器上所需的工作时间

2.对每一种可能进行穷举,以实现所有的可能

3.建立每一种可能的工作流程和时间计算模型

4.打印所有的可能,并对所有的可能所得的结果进行筛选,获得最优结果


解决问题

根据前面的分析,具体化问题,假设题目如下:

python 遗传算法 流水车间调度 python 流水作业调度_Python

对以上的几点,其中1,2,4点:

1和4 只是简单的逻辑处理,

2可以通过调用全排列函数进行实现,则有(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)共3!=6种可能;

在第3点中,需要建立计算模型:

定义两个变量sum_m1,sum_m2,分别用于存储机器1和机器2的工作记录

取其中一种可能(1,2,3),其中,作业1在M1的时间为HK1_M1,在M2的时间为HK1_M2,

定义两个列表,f1和f2,f1[i],f2[i]分别代表作业i在机器1,机器2的完成的那刻的时间(时间刻度),以此类推;

情景模拟开始,有如下图

python 遗传算法 流水车间调度 python 流水作业调度_全排列_02

将作业1投入到机器M1时,则sum_m1 = HK1_M1=2,f1[1]=2

根据之前结论,机器2有空闲和工作两种状态:

这里是机器2是空闲的状态,所以sum_m2=sum_m1+HK1_M2=3,f2[1]=3

但是如果机器2不是空闲的状态的话,则需要等待,如下,当作业1在机器M1完成时,机器M2正在工作,所以需要等待

python 遗传算法 流水车间调度 python 流水作业调度_数学建模_03

所以可以有以下结论:

当sum_m1>=sum_m2时,sum_m2=sum_m1+HK[i]_M2

否则,即机器2正在工作,已在机器M1完成的作业需要等待机器M2

sum_m2=sum_m2+HK[i]_M2,f2[i]=sum_m2

例如这里:

当作业3完成时

sum_m1=2,f1[3]=2,sum_m2=2+3=5,f2[3]=5

当作业1完成时

sum_m1=2+2=4,f1[1]=4,

sum_m2=5>sum_m1=4,所以sum_m2=sum_m2+HK[1]_M2=6

之后也是如此处理


编程


编程流程以及数据类型选择

题目条件的准备

定义2个字典,用于存储作业i的在机器M1和机器M2所需的时间

dict_a = {1:HK1_M1,2:HK2_M1,...,i:HKi_M1}

dict_b = {1:HK1_M2,2:HK2_M2,...,i:HKi_M2}

题目求解时的对象定义

定义1个列表,用于存储所有的作业排列可能;

选择列表用于存储每一个作业的在机器M1和M2的完成时的时间刻度

f1=['','',...,''] ,f2=['','',...,''],则f2列表的最后一个元素即是题目的答案

还有其它不再赘述


发现问题以及解决


最终实现

程序代码:

#author:zhj
#time:19.11.24
#versions:1.0
#question:流水作业调度问题
#全排列函数
per_result = []
def PrintLst(lst):
    for i in lst:
        print(i,end=" ")
#find the more index
lst_index = []
def find_index(lst,ele):
    for i in range(len(lst)):
        if(lst[i] == ele):
            lst_index.append(i)
def per(lst,s,e):
    if s == e:
        per_result.append(list(lst))
    else:
        for i in range(s,e):
            lst[i],lst[s] = lst[s],lst[i]#试探
            per(lst,s+1,e)#递归
            lst[i],lst[s] = lst[s],lst[i]#回溯
result_tuple_f = []
def get_f1_f2(lst,dict_a,dict_b):
    sum_m1 = 0
    sum_m2 = 0
    f1 = ['' for i in range(len(lst))]
    f2 = ['' for i in range(len(lst))]
    for i in range(len(lst)):
        ele = lst[i]
        sum_m1 += dict_a[ele]
        f1[i] = sum_m1
        if(sum_m2 <= sum_m1):
            sum_m2 = sum_m1+dict_b[ele]
        else:
            sum_m2 = sum_m2+dict_b[ele]
        f2[i] = sum_m2
    tuple_f = (f1,f2)
    result_tuple_f.append(tuple_f)
# lst_id = [1, 2, 3,4]
#get the args
num = input("请输入作业的个数:")
lst_id = [i+1 for i in  range(int(num))]
a = []
b = []
for i in range(int(num)):
    print("请输入作业{}在机器M1,M2需要的时间".format(i+1))
    input_m1_time = eval(input("M1["+str(i+1)+"]="))
    a.append(input_m1_time)
    input_m2_time = eval(input("M2["+str(i+1)+"]="))
    b.append(input_m2_time)
    print("")
dict_a = dict(zip(lst_id,a))
dict_b = dict(zip(lst_id,b))
per(lst_id,0,len(lst_id))
#[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
#print(per_result)#success
for i in per_result:
    #print(i)#success
    get_f1_f2(i,dict_a,dict_b)
#[([2, 5, 7], [3, 6, 10]), ([2, 4, 7], [3, 7, 8]), 
# ([3, 5, 7], [4, 6, 10]), ([3, 5, 7], [4, 8, 9]), 
# ([2, 5, 7], [5, 6, 8]), ([2, 4, 7], [5, 6, 8])]
#print(result_tuple_f)#success
good_bestf = result_tuple_f[0][1][-1]#将第1个的bestf赋值给good_bestf
lst_bestf = []
print("求解过程:")
for i in range(len(result_tuple_f)):#per_result[i],result_tuple_f[i][1]
    #print("\t一个解:bestf={},调度方案:{},f2:{}".format(result_tuple_f[i][1][-1],per_result[i],result_tuple_f[i][1]))
    print("\t一个解:bestf=",end="")
    print(result_tuple_f[i][1][-1],end=" ")
    lst_bestf.append(result_tuple_f[i][1][-1])
    print("调度方案:",end="")
    PrintLst(per_result[i])
    print("f2:",end=" ")
    PrintLst(result_tuple_f[i][1])
    print("")
    if(good_bestf < result_tuple_f[i][1][-1]):
        good_bestf = good_bestf
    elif(good_bestf > result_tuple_f[i][1][-1]):
        good_bestf=result_tuple_f[i][1][-1]
    # else:
    #     good_lst = good_lst
print("求解结果:")
print("最少时间:",end="")
print(good_bestf)
#find the bestf index
find_index(lst_bestf,good_bestf)
# return 
print("最优调度方案:有{}种,如下:".format(len(lst_index)))
it = 0
for i in lst_index:
    it +=1
    print("方案{}:".format(it))
    PrintLst(per_result[i])
    print(" ")

运行结果截图:

python 遗传算法 流水车间调度 python 流水作业调度_全排列_04


总结

这道题的关键在于数学建模过程,即机器时间的计算。这道题目中,注意所求的最后的机器M2完成最后一个作业的时间刻度。


程序缺陷以及完善

大量的对象定义,既让整个程序结构明了,但又让整个程序显得臃肿。在之后的解题中,会考虑使用第三方库numpy进行解题

另外会在以后中,使用动态规划法重新解决这一道题。


解题心路历程

大量的对象,让整个程序的结构可能有些复杂。但编写过程还算流畅,难就难在数学建模,参考了很多博客,发现很多博客在数学建模过程中的逻辑是错误的,所以数学建模过程,花费的时间很多,也借助了Excel表格工具进行建模。


本篇未完成,会抽出时间进行更新。如有更改,会在此列出

本篇已完成。——2019.12.27