回溯法解決流水作業調度問題(全排列+計算模型+剪枝函數)

同系列算法問題

回溯法解決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.打印所有的可能,並對所有的可能所得的結果進行篩選,獲得最優結果


解決問題

根據前面的分析,具體化問題,假設題目如下:

對以上的幾點,其中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的完成的那刻的時間(時間刻度),以此類推;

情景模擬開始,有如下圖

將作業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正在工作,所以需要等待

所以可以有以下結論:

當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(" ")

運行結果截圖:


總結

這道題的關鍵在於數學建模過程,即機器時間的計算。這道題目中,注意所求的最後的機器M2完成最後一個作業的時間刻度。


程序缺陷以及完善

大量的對象定義,既讓整個程序結構明瞭,但又讓整個程序顯得臃腫。在之後的解題中,會考慮使用第三方庫numpy進行解題

另外會在以後中,使用動態規劃法重新解決這一道題。


解題心路歷程

大量的對象,讓整個程序的結構可能有些複雜。但編寫過程還算流暢,難就難在數學建模,參考了很多博客,發現很多博客在數學建模過程中的邏輯是錯誤的,所以數學建模過程,花費的時間很多,也藉助了Excel表格工具進行建模。


本篇未完成,會抽出時間進行更新。如有更改,會在此列出

本篇已完成。——2019.12.27

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章