python如何利用演演算法解決業務上的【分單問題】

2023-05-25 06:01:17

  分單是很多企業日常工作中非常典型的一項內容,它非常複雜,但同時又極為重要,如何合理的分單是企業管理中一個很重要的課題。

  之所以說分單很複雜,是因為影響單據該分給誰,分多少量這個事兒本身就有太多的影響因子;一旦管理者在分單的公平性上出現大的問題,厚此薄彼了,往大了講,員工的做事積極性有可能都會被挫傷。

  簡單列舉幾類:

    1、員工的技能矩陣;

    2、員工的出勤資料;

    3、各種型別單據的SPT(標準作業時間);

    4、員工手中已有的單據量;

    5、不同型別單據量的分佈情況;

    6、某種型別的單據不夠分時,該優先分給誰;

    7、每次單據的來件數量、來件的型別高度不確定;

    8、當某種型別給某些員工少分了一些數量,如何在另外一些型別上儘量多分他一些,使得每個人分到的總量儘可能均衡;

    9、單據的黑白名單策略(某些型別,某些申請人提的優先不分給哪些人,優先分給哪些人);

    10、某個單據被被駁回過,如何在第二次分單時優先分給當初的處理人;

    11、某些人可以同時處理兩個環節,如何保證他不處理同一個單據的前後兩個環節,避免內控管理失效;

    12、多個領單小組共用某些流程埠,如何區分單據優先分給哪個組;

    13、如何保證每個人能處理能力範圍內的儘可能多的型別的單據,以確保員工的能力得到充分鍛鍊;

    ……

   您瞅瞅,我覺沒撒謊,這可一點都不簡單。偏偏在小爬我的實際業務中,就被要求解決這麼頭疼的問題。但是你以為小爬我這就怕了?NO NO NO,需求越有挑戰,小爬越高興。

  先說說如何解決每個人分配的單據量不均衡的問題,其中一種思路:我們把總單據量基於業務型別分成N組,然後基於每個員工能處理的業務型別和當天的出勤情況,找到能處理每種業務型別的人員列表,假設是M個人。我們可以利用演演算法,每次分單前將人員列表的順序隨機打亂,那麼每次分單時,每個人都有一定概率出現在佇列末尾,也就是單據不夠分給他的情況,最終只要我們把N組型別的單據都分完時,大家的概率總體上是均衡的,不過概率的事兒沒人能說它絕對公平。

下面這段程式碼,就可以完美地打亂某個列表的順序,讓它徹底重排:

import random
def shuffle_list(lst):
    '''將列表的順序隨機打亂,得到一個新的列表'''
    new_lst = lst[:]  # 複製原列表,否則會影響原列表
    random.shuffle(new_lst)  # 打亂順序
    return new_lst

  可是它依然不是絕對公平的,沒法保證每個人分到的單據量儘可能一樣多,因為它只是在模擬扔色子,寄希望於概率均勻分佈。如何保證大家的數量相對公平呢?

  我們不妨利用python的字典,儲存每個人(key)已經分到的單據號的列表合集,把列表作為字典的Value。當我們在對某種型別的N個單據分給M個人時,就好比是發撲克牌,但是又不能簡單去按順序發牌,而是看誰手上的牌少,就優先把剩餘的牌發給它,這樣手上牌少的人,總是能被照顧,直到最後大家手上發到的牌數接近。如何知道這個特殊的字典中,到底哪個Key的牌最少(列表的長度最短)呢?您不妨參考下下面的演演算法。

def get_Dic_shortest_key(keyList,my_dict):
    '''根據輸入的字典,字典的鍵對應的值都是一個列表,如果哪個鍵對應的列表的長度最短,則返回該鍵'''
    shortest_key = None
    shortest_length = float('inf')
    for key, value in my_dict.items():
        if key in keyList and len(value) < shortest_length:
            shortest_key = key
            shortest_length = len(value)
    # Return the key with the shortest list value
    return shortest_key

  有了上面的演演算法,我們也就解決了每次的發牌優先分給誰的問題,怎麼把牌發完到對的人手上,就不是個問題了,程式碼範例如下:

def seperate_list(lst,personList, personFormNumber_dic):
    """
    將一個列表拆分成n個子列表組成的二維列表,要求每個子列表元素的長度儘可能接近
    :param lst: 待分配的單據號列表
    :param personList: 可以參與此次分單的人員列表
    :param personFormNumber_dic:字典結構,每個人key手上已有的單據列表value
    :return: personFormNumber_dic 返回這個字典(每個人手上的單據清單)
    """
    m = len(lst)
    for i in range(m):  # 從後往前遍歷列表
        targetPerson=""
        targetPerson=get_Dic_shortest_key(personList,personFormNumber_dic)
        for random_element in lst:
            personFormNumber_dic[targetPerson].append(random_element)
            lst.remove(random_element)
            break
    return personFormNumber_dic

  希望這些對現實業務的思考和程式碼實現,能對您的工作有所啟發。不管咋說,活到老,學到老。拒絕躺平,一起捲起來!

快來關注本公眾號 獲取更多爬蟲、資料分析的知識!