華中杯 數學建模 A題簡單覆盤(附Python原始碼)

專注的阿熊發表於2022-08-09

import random

import pandas as pd

import time

# 這裡定義一個全域性變數,用來表示最大的貨架數——即批次種類限制

N = 200

# 然後是寫一個函式,實現只儲存同一訂單對應的貨品種類

def big_and_small(choose0, order):

     """

     該函式用於在未保留的訂單中劃分大訂單和小訂單,大訂單佔 1/3

     :param choose0:

     :param order:

     :return:

     """

     big1 = []

     small0 = []

     choose0 = dict(choose0)

     # 選取的訂單字典,鍵是訂單號,值是種類數

     choose_order = {}

     for i1 in choose0:

         if choose0[i1] == 0:

             choose_order[i1] = len(order[i1])

     # 將未被選的訂單字典排序

     choose_order = sorted(choose_order.items(), key=lambda x: x[1])

     new_len = len(choose_order)

     # 計算大小分界的索引

     mid_index = new_len * 2 // 3

     index0 = 0

     for i1 in choose_order:

         if index0 < mid_index:

             small0.append(i1[0])

         else:

             big1.append(i1[0])

         index0 += 1

     return big1, small0

# 寫一個函式判斷是否在當前批次

def is_in(batch, order):

     """

     其實應該是得出新增訂單的新增貨品種類數

     :param batch: 當前批訂單 , 內容是包含的貨品

     :param order: 當前訂單,也是貨品

     :return: 新增貨品種類數

     """

     # 將批次和訂單都列表化

     batch = list(batch)

     order = list(order)

     ans = 0

     # 迴圈累加不在當前批次的貨品種類數

     for o0 in order:

         if o0 not in batch:

             ans += 1

     return ans

# 寫一個選取函式

def choose_batch(orders1, batch01, batch02, keys0):

     """

     該函式用於在大訂單集或者小訂單集將這些訂單實現分批

     引入洗牌演算法,將選取的訂單的順序進行打亂隨機

     :param orders1: 訂單

     :param batch01: 批次列表 1 ,儲存批次對應的訂單

     :param batch02: 批次列表 2 ,儲存批次對應的貨品種類

     :param keys0: 選取訂單的列表 , 內容是訂單號

     :return: 返回選取加入後的批次 1 2

     """

     # 寫一個洗牌演算法

     # 先獲取選取訂單的長度

     # keys_len = len(keys0)

     # # 逆序遍歷選取列表

     # # 透過交換隨機下標來實現將選取的訂單的順序洗牌

     # for k in range(keys_len - 1, -1, -1):

     #     # 獲取隨機下標

     #     rindex = random.randint(0, k)

     #     # 交換位置

     #     temp = keys0[rindex]

     #     keys0[rindex] = keys0[k]

     #     keys0[k] = temp

     batch1 = dict(batch01)

     batch2 = dict(batch02)

     # 獲取到當前最大批次數對應的下標

     i0 = len(batch1) - 1

     # 如果此時沒有,這需要置為 0

     if i0 < 0:

         i0 = 0

     for key0 in keys0:

         # 初始時需要建立批次

         if i0 not in batch1:

             batch1[i0] = []

             batch2[i0] = []

         add_type = -1

         index0 = -1

         flag = False

         for j in range(i0 + 1):

             if key0 in batch1[j]:

                 flag = True

                 break

             at = is_in(batch2[j], orders[key0])

             # 完全重合直接退出迴圈

             if at == 0:

                 index0 = j

                 break

             if (add_type < 0 or add_type >= at) and len(batch2[j]) + at <= N:

                 add_type = at

                 index0 = j

         if flag:

             continue

         if index0 >= 0:

             batch1[index0].append(key0)

             for good in orders1[key0]:

                 if good not in batch2[index0]:

                     batch2[index0].append(good)

         else:

             i0 += 1

             batch1[i0] = []

             batch1[i0].append(key0)

             batch2[i0] = orders1[key0]

     return batch1, batch2

# 然後接下來寫一個從小訂單中選取

def choose_small(orders1, batch1, batch2, small1):

     orders1 = dict(orders1)

     keys = list(small1)

     batch1 = dict(batch1)

     batch2 = dict(batch2)

     batch1, batch2 = choose_batch(orders1, batch1, batch2, keys)

     return batch1, batch2

# 這個是在大的陣列中重新選取

def choose_big(big1, orders1, batch, batch2):

     orders1 = dict(orders1)

     keys = list(big1)

     # 批次陣列有兩種,一種是儲存訂單,一種是儲存貨品

     batch1 = dict(batch)

     batch2 = dict(batch2)

     batch1, batch2 = choose_batch(orders1, batch1, batch2, keys)

     return batch1, batch2

# 然後寫一個迭代函式

def gen_ite(order, btch_1, btch_2):

     """

     參考遺傳演算法的精英基因保留的思想,我們在迭代的過程中將批次中訂單數較多的保留,這裡每次選取 10% 的批次

     然後需要將這些批次標記為已經選擇然後重新在為選取的訂單集劃分大小訂單

     選取的批次可以直接新增在下一代的開頭

     :param order: 訂單字典,訂單號對應相應的貨品種類

     :param btch_1:

     :param btch_2:

     :return: 最終多次迭代後的結果

     """

     btch_1 = dict(btch_1)

     btch_2 = dict(btch_2)

     for i0 in range(1):

         # 先對批次的訂單數排序 —— 降序排序

         btch = sorted(btch_1.items(), key=lambda x: len(x[1]), reverse=True)

         btch_len = len(btch)

         index0 = btch_len // 5

         # 生成兩個新的字典,和 btch1 2 類似

         bt1 = {}

         bt2 = {}

         # 先將精英保留

         # 選擇字典

         choose_0 = {}

         for j in order.keys():

             choose_0[j] = 0

         for k in range(index0):

             for o0 in btch_1[k][1]:

                 choose_0[o0] = 1

             bt1[k] = btch_1[btch[k][0]]

             bt2[k] = btch_2[btch[k][0]]

         keys = []

         for m in order.keys():

             if choose[m] == 0:

                 keys.append(m)

         # # 接下來劃分大小訂單

         # big1, small1 = big_and_small(choose, orders)

         # print('big: %d small: %d' % (len(big1), len(small1)))

         # # 之後根據劃分的大小訂單在得到新的分批

         # bt1, bt2 = choose_big(big1, orders, bt1, bt2)

         # print(len(bt1), len(bt2))

         # # bt1, bt2 = choose_small(orders, bt1, bt2, small1)

         # # print(len(bt1), len(bt2))

         bt1, bt2 = choose_batch(order, bt1, bt2, keys)

         # 然後比較新舊分批的批數來更新批次

         if len(bt1) < len(btch_1):

             btch_1 = bt1

             btch_2 = bt2

             print(' %d , 批次數為 %d' % (i0 + 1, len(btch_1)))

         le = 0

         for bi in btch_1:

             le += len(btch_1[bi])

         print(le)

     return btch_1, btch_2

# 寫一個計算距離的函式

def compute_dis(order, batch10, goods):

     """

     :param order: 其實就是訂單集合,透過訂單可以知道需要的貨品

     :param batch10: 批次下的訂單列表

     :param goods: 當前批次下的貨物關於貨物列表的索引

     :return: 返回距離和

     """

     order = dict(order)

     batch10 = list(batch10)

     goods = dict(goods)

     dis = 0

     for bat in batch10:

         index_max = -1

         index_min = -1

         for good in order[bat]:

             if index_min == -1 and index_max == -1:

                 index_max = index_min = goods[good]

             else:

                 index_max = max(index_max, goods[good])

                 index_min = min(index_min, goods[good])

         dis += index_max - index_min

     return dis

# 寫一個第二問的解決函式

def solution2(order, batch2, batch1):

     batch1 = dict(batch1)

     batch2 = dict(batch2)

     order = dict(order)

     pre_total = 0

     ans = []

     p = 0

     total = 0

     start0 = time.time()

     for batch in batch2.items():

         # 現在是對每一個批次

         # 先建立貨物種類到下標對映 , 和下標到貨物的對映

         good_to_index = {}

         index_to_good = batch[1]

         for index1 in range(len(index_to_good)):

             good = index_to_good[index1]

             good_to_index[good] = index1

         dis = compute_dis(order, batch1[batch[0]], good_to_index)

         print(' 當前第 %d 批原始距離為 %d' % (p + 1, dis))

         pre_total += dis

         for i0 in range(200000):

             j = random.randint(0, len(index_to_good) - 1)

             k = random.randint(0, len(index_to_good) - 1)

             if j == k:

                 continue

             # 獲取貨物資訊

             good1, good2 = index_to_good[j], index_to_good[k]

             # 先交換

             temp = good_to_index[good1]

             good_to_index[good1] = good_to_index[good2]

             good_to_index[good2] = temp

             if dis > compute_dis(order, batch1[batch[0]], good_to_index):

                 index_to_good[j], index_to_good[k] = good2, good1

                 dis = compute_dis(order, batch1[batch[0]], good_to_index)

             else:

                 good_to_index[good1], good_to_index[good2] = good_to_index[good2], good_to_index[good1]

         print(' 迭代後 第 %d 批原始距離為 %d' % (p + 1, dis))

         total += dis

         ans.append(index_to_good)

         p += 1

     print(' 原始距離 ', pre_total)

     print(' 最終距離 ', total)

     end0 = time.time()

     print(' 時間消耗 ', end0 - start0)

     return ans

# 找到訂單的起始點

def find_min_and_max_index(order_good, good_index):

     order_good = list(order_good)

     good_index = dict(good_index)

     min_index = max_index = -1

     for good_name in order_good:

         if min_index == -1 and max_index == -1:

             min_index = max_index = good_index[good_name]

             continue

         min_index = min(min_index, good_index[good_name])

         max_index = max(max_index, good_index[good_name])

     return min_index, min_index

def right_find(order, batch1, good_index, pos):

     order = dict(order)

     batch1 = list(batch1)

     good_index = dict(good_index)

     ans = ''

     j = int(pos)

     tar = j

     for name in batch1:

         i1, i2 = find_min_and_max_index(order[name], good_index)

         if i1 >= pos and (j > i1 or j == pos):

             j = i1

             ans = name

             tar = i2

     return ans, tar

def left_find(order, batch1, good_index, pos):

     order = dict(order)

     batch1 = list(batch1)

     good_index = dict(good_index)

     ans = ''

     j = pos

     tar = j

     for name in batch1:

         i1, i2 = find_min_and_max_index(order[name], good_index)

         if i1 <= pos and (j < i1 or j == pos):

             j = i2

             ans = name

             tar = i1

     return ans, tar

def find_people(distance):

     """

     找工人函式

     我們根據方向和工人所處的位置決定,選取哪個工人

     :param distance: 距離陣列

     :return: 返回工人的下標

     """

     # 答案初始化為下標 0

     ans = 0

     distance = list(distance)

     for i1 in range(1, len(distance)):

         if distance[i1] < distance[ans]:

             ans = i1

     return ans

def solution3(order, batch1, batch2, n):

     """

     該函式主要用於求解第三問

     :param order: 訂單對應的貨品,字典物件

     :param batch1: 分批好的訂單,批號對應的訂單

     :param batch2: 分批好的訂單對應的貨品種類,批號對應的種類

     :param n: 工人的數量

     :return: 返回第三問需要的結果

     """

     # 先將這些字典物件示例化,實現可變引數在函式內和外的分離

     batch1 = dict(batch1)

     order = dict(order)

     batch2 = dict(batch2)

     # 答案,用列表儲存,每一個其實也是列表,分別對應答案需要的答案

     ans = []

     peo_dis1 = []            # 用來計算每一批員工的總路程

     total_dis0 = []          # 總距離

     for i1 in range(n):

         total_dis0.append(0)

     # 我們遍歷批次

     for i1 in batch1.keys():

         # 例項化批次對應的訂單和貨品種類,這裡其實還是將其變為陣列的形式

         bat_or = list(batch1[i1])

         bat_go = list(batch2[i1])

         # 貨品在該批次中對應的位置, 即貨架的位置

         good_index = {}

         # 遍歷一遍訂單和貨品種類,這裡的順序其實就是第二問求解的擺放順序

         for ind in range(len(bat_go)):

             # 先獲取當前的貨物

             good = bat_go[ind]

             # 利用字典的對映關係,設定貨物對應的擺放位置的關係

             good_index[good] = ind

         people = []         # 工人列表,用來儲存工人的位置

         task = []           # 任務列表,用來儲存工人當前批乾的第幾個訂單

         peo_dis0 = []       # 當前批的距離

         # 先將這兩個列表都初始化為 0

         for p in range(n):

             people.append(0)

             task.append(0)

             peo_dis0.append(0)

         # 當批次中的訂單還有的時候,需要進行分配

         while len(bat_or) > 0:

             # 我們可以先進行正向的尋找 , 先找當前最小的下標,同級優先按工人編號尋找

             p = find_people(peo_dis0)

             # 獲取當前工人的位置

             pos = people[p]

             # 然後在以當前工人位置為起點,向右尋找最近的訂單,並返回訂單號和訂單完成時工人的位置

             name, next_pos = right_find(order, bat_or, good_index, pos)

             # 如果找不到訂單,說明右邊沒有訂單了,需要逆向尋找

             if name == '':

                 # 向當前位置最左邊找離最近的訂單

                 name, next_pos = left_find(order, bat_or, good_index, pos)

             task[p] += 1 外匯跟單gendan5.com       # 當前工人的任務數 + 1

             temp = [name, i1 + 1, p + 1, task[p]]        # 當前行的答案

             ans.append(temp)        # 將這一行的答案新增到最終答案中

             bat_or.remove(name)     # 將這個訂單從列表中移除

             print(next_pos)

             # 計算移動距離

             dis = abs(pos - next_pos)

             # 加和

             peo_dis0[p] += dis

             people[p] = next_pos

         peo_dis1.append(peo_dis0)

         for ik in range(n):

             total_dis0[ik] += peo_dis0[ik]

     # 返回答案

     return ans, peo_dis1, total_dis0

if __name__ == "__main__":

     start = time.time()

     # 先寫一個檔案路徑

     filepath = ' 附件 1 :訂單資訊 .csv'

     # 先讀取檔案

     # 使用 pandas 內建的讀取方式

     df = pd.read_csv(filepath)  # 將檔案讀取為一個 dataframe 格式

     con = pd.DataFrame(df, columns=['OrderNo', 'ItemNo'])

     orders = {}

     for line in con.index:

         if not con.iloc[line]['OrderNo'] in orders:

             orders[con.iloc[line]['OrderNo']] = []

         orders[con.iloc[line]['OrderNo']].append(con.iloc[line]['ItemNo'])

     s = set(orders.keys())

     print(len(s))

     # 選取字典,表示對應訂單是否被選取

     choose = {}

     # 初始化為未被選取

     for key in orders.keys():

         choose[key] = 0

     big, small = big_and_small(choose, orders)

     print(len(big), len(small))

     b1, b2 = choose_big(big, orders, {}, {})

     b1, b2 = choose_small(orders, b1, b2, small)

     end = time.time()

     print(end - start)

     print(len(b1), len(b2))

     btch1, btch2 = dict(b1), dict(b2)

     file = open('result1.csv', 'w+')

     result1 = 'OrderNo,GroupNo\n'

     for b in btch1.keys():

         for o in btch1[b]:

             result1 += o + ',' + str(b + 1) + '\n'

     print(result1, file=file)

     file.close()

     ans2 = solution2(orders, batch1=btch1, batch2=btch2)

     result2 = 'ItemNo,GroupNo,ShelfNo\n'

     for index in btch2.keys():

         btch2[index] = ans2[index]

         for _i in range(len(ans2[index])):

             result2 += ans2[index][_i] + ',' + str(index + 1) + ',' + str(_i + 1) + '\n'

     file = open('result2.csv', 'w+')

     print(result2, file=file)

     file.close()

     ans3, peo_dis, total_dis = solution3(orders, batch2=btch2, batch1=btch1, n=5)

     result3 = 'OrderNo,GroupNo,WorkerNo,TaskNo\n'

     for a in ans3:

         for index in range(len(a)):

             result3 += str(a[index])

             if index + 1 < len(a):

                 result3 += ','

             else:

                 result3 += '\n'

     file = open('result3.csv', 'w+')

     print(result3, file=file)

     file.close()

     result4 = ''

     for b in peo_dis:

         for index in range(len(b)):

             result4 += str(b[index])

             if index + 1 < len(b):

                 result4 += ','

             else:

                 result4 += '\n'

     file = open(' 員工路程,單批 .csv', 'w+')

     print(result4, file=file)

     file.close()

     file = open(' 員工路徑總和 .csv', 'w+')

     result4 = ''

     for i in range(len(total_dis)):

         result4 += str(total_dis[i])

         if i + 1 < len(total_dis):

             result4 += ','

         else:

             result4 += '\n'

     print(result4, file=file)

     file.close()


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2909701/,如需轉載,請註明出處,否則將追究法律責任。

相關文章