第十四届华中杯大学生数学建模挑战赛A题Python解答共51批(附文件源码链接)需者自取

问题重述:

A

分拣系统优化问题

某电商公司配送中心的工作流程分为统计汇总、转运上架、按订单分拣、核对打包等

步骤。其中,分拣环节操作复杂,耗时较长,其效率是影响配送中心整体性能的关键因素。

首先,系统统计汇总出当天全部待配送订单所包含的所有货品及相应数量。然后,转运工

将这些货品由仓库转运至分拣处,并放置到货架上,等待分拣。上架时,一个货架中仅放

置同一种货品。为简化问题,不考虑货架的容积和载重限制,即每个货架能够放置的货品

件数没有限制。最后,分拣工按任务单依次分拣出每一个订单包含的货品。

例如,某天共有
5
个订单,订单的信息如表
1
(原始数据文件格式见附件
1
格式说明)

所示。


1
:订单信息示例

订单编号

所含货品种类及相应数量

D0001

P0001
×
2, P0002
×
1, P0003
×
1, P0004
×
1

D0002

P0003
×
1, P0004
×
1, P0005
×
1, P0006
×
3

D0003

P0001
×
1, P0003
×
1, P0005
×
1, P0007
×
1

D0004

P0002
×
1, P0004
×
1, P0006
×
1, P0008
×
3

D0005

P0001
×
1, P0003
×
2, P0004
×
1, P0007
×
1

系统统计出这
5
个订单共包含
8

26
件货品,分别为
4

P0001

2

P0002

5

P0003

4

P0004

2

P0005

4

P0006

2

P0007

3

P0008
。转运工将这些货

品分别放置在
8
个货架上。分拣工根据任务单上的订单信息,依次分拣出每一个订单的货

品。

随着该公司业务量不断增长,未来有可能出现当天待配送订单所含货品种类数大于当

前货架数量

的情况(假设任何一个订单所含货品种类数均小于货架数量

)。但是,

由于受到场地和成本的限制,很难直接增加货架。该公司希望在你们团队的协助下解决该

问题,并尝试提高分拣效率。具体解决如下问题。

1.
设计分批算法
:将当日订单分为多个批次。要求每个批次的订单所含货品种类数均不

超过

,且批次越少越好(相应转运次数也越少,效率越高)。针对附件
1
中的订单

信息,应用你们的算法,计算当货架数量

时最少的批次数,给出每批订单数

量、货品种类数、分批方案等结果,并将完整原始分批方案按指定格式输出到文件

result1.csv
中,格式要求见附件
2

问题一解题思路(其他思路在链接里)

第一步:根据附件的订单信息,提取每个订单的货品种类数存入列表df_pv_list中,提取每个订单的编号存入列表orderno_list中,这两个列表中的元素个数一致,且每个订单编号对应的货品种类数在两个列表中的索引一致。例如,列表df_pv_list的第一个元素df_pv_list[0] =’D0001’对应的货品种类数,应为列表orderno_list的第一个元素orderno_list[0]=19种,即两个列表的索引值相互关联或者说完全一一对应。

第二步:根据附件的订单信息,创建一个‘键’由订单号组成,‘值’由订单号对应的的货品种类列表组成的字典dic。

第三步:从列表df_pv_list中获取一个最大值,即max_values = max(df_pv_list),通过index()方法得到max_values对应的索引值,即index_values = df_pv_list.index(max_values),由于列表df_pv_list与列表orderno_list的索引值相对应,可以得到对应的订单编号orderno_max = orderno_list[index_values]。由订单编号orderno_max从字典dic中获取该订单编号对应的货品种类依次存入batch_s列表中,并统计其中货品种类个数n。

第四步:用pop()方法将max_values从列表df_pv_list中移除,即df_pv_list.pop(index_values),由于在列表df_pv_list中max_values可能不止一个,为了保证列表df_pv_list和列表orderno_list元素严格一一对应,此处移除只能用pop()方法,根据索引值去移除元素。再将订单编号orderno_max从列表orderno_list中移除,即orderno_list.remove(orderno_max),此处移除操作可以使用remove()方法,因为列表orderno_list中的元素唯一,可以根据值去移除。

第五步:使用循环逐个从列表orderno_list取出订单编号,将取出来的订单编号作为‘键’从字典dic中得到每个订单对应的货品种类列表,再嵌套循环将每个订单对应的货品种类列表里的每个元素与列表batch_s里的每个元素相比较,设置计数器count,表示每个订单对应的货品种类与列表batch_s里的货品种类相同的件数。如果有相等的元素对count进行加1操作。将所有count值存入列表num_com中,注意:列表df_pv_list,列表orderno_list与列表num_com的数据是严格一一对应的,即相同索引从三个列表取的值是一一对应的。

第六步:判断n是否小于200,若是,则获取最大货品相同的件数max_num_com = max(num_com),获取对应的索引max_num_com_index = num_comt.index(max_num_com),获取对应的订单编号orderno_m = orderno_list[max_num_com_index],将获取的订单编号作为‘键’从字典dic中得到每个订单对应的货品种类列表,求其列表长度得到货品种类数num,用货品种类数num减去列表num_com对应的货品相同件数得到不相同件数,即num_none = num – num_com[max_num_com_index],并将该货品列表里的每个元素与batch_s列表中的元素一一比较,将不同的元素加入进batch_s列表中,再重新统计batch_s列表中的货品种数n。

第七步:创建批次列表batch_list(在第四步,执行移除操作之前应将订单orderno_max添加到该列表中)。将max_values赋给n,比较n是否小于200,若小于,将订单编号存入列表batch_list中,即batch_list.append(orderno_m),执行n =n +num_none操作。再将列表df_pv_list中对应的值移除,即df_pv_list.pop(max_num_com_index),将列表orderno_list中对应的值移除,即orderno_list.remove(orderno_m),将列表num_com设置为空;重复第五步至第七步操作直到n大于等于200;若大于,批次操作完成,打印出batch_list列表信息,将列表num_com设置为空,将列表batch_s设置为空,重复执行第三步至第七步操作。每次重复执行第三步至第七步操作时,都需判断列表df_pv_list和列表orderno_list列表是否为空。若为空,则所有订单都已处理完成,将保存在列表batch_list中的数据依批次打印出来即可,再将其输出至result1.csv文件中。否则继续执行重复操作。

分批算法Python实现:

创建每个订单的货品种类数量列表df_pv_list

创建每个订单编号列表orderno_list

创建每个订单与之对应的货品种类列表的字典dic

创建每批次列表batch_list

创建总批次列表sum_batch_list

创建每批次货品种类数列表batch_s

创建总批次货品种类数列表sum_batch_s

创建订单的货品种类的相同数列表num_com

  1. 定义getMax_per_batch_info(n)方法,有1个形参,用来传入每批货品种类的值n。该方法的功能为经计算后返回列表batch_s货品种类数和batch_s货品列表,共返回两个值。
  2. 定义get_num_com(i_l_m)方法,有1个形参,分别以getMax_per_bath_info()方法返回的batch_s值作为实参传入。该方法功能为计算其他订单与batch_s列表中货品种类相同的件数,并将其结果存入num_com列表中。
  3. 定义get_bath_info(n,num_com)方法,有2个形参,将getMax_per_batch_info(n)方法的返回值n作为实参传入。该方法有2个while循环,判断n是否小于200,若小于,则将最大相同件数订单的货品种类不存在于batch_s列表中货品种类依次存入batch_s列表中,判断batch_s列表长度是否大于200,若大于,跳出循环;若小于,则继续调用getMax_per_batch_info(n)方法和get_num_com(i_l_m)方法;直至列表batch_s中货品种类数等于200,循环结束。输出批次信息并保存至batch_list列表中,并将已经处理的订单从列表orderno_list列表中移除。

        4、主程序为利用for循环调用以上三个方法,每循环1次应得到1个批次结果,将每次循环得到的结果存入sun_bacth_list列表中,最后将最终结果转换为DataFrame结构数据,输入到result1.csv文件中。应有判断语句,判断orderno_list列表是否为空,若为空,则结束程序;否则,继续执行。、

python代码如下:

'''
设计分批算法:将当日订单分为多个批次。要求每个批次的订单所含货品种类数均不
超过 ,且批次越少越好(相应转运次数也越少,效率越高)。针对附件 1 中的订单
信息,应用你们的算法,计算当货架数量 时最少的批次数,给出每批订单数
量、货品种类数、分批方案等结果,并将完整原始分批方案按指定格式输出到文件
result1.csv 中
'''

import pandas as pd

#将数据读取进来
df = pd.read_csv(r'D:\Pycharm\pythonProject\data.csv')
#将原始数据csv文件处理一下,生存OrderNoSum.csv(已放入了附件中),用来获取每个订单编号
df1 = pd.read_csv(r'D:\Pycharm\pythonProject\OrderNoSum.csv')
orderno_list = []
for i in df1['OrderNo'].values:
    orderno_list.append(i)
#统计每个订单的货品种类数量
df_pv = df.groupby('OrderNo')['ItemNo'].count()
#将每个订单的货品种类数量存入列表中
df_pv_list=[]  #923个值
for i in df_pv.values:
    df_pv_list.append(i)
#获取每个订单货品的编号与其种类数量相对
itemno_list = df['ItemNo'].values
#将每个订单的货品种类改成键值对的形式
dic = {}
sum_x = 0
dic.setdefault('{}'.format(orderno_list[0]),'{}'.format(list(itemno_list[0:df_pv_list[0]])))
for i in range(1,923):
    sum_x = sum_x + df_pv_list[i-1]
    dic.setdefault('{}'.format(orderno_list[i]),'{}'.format(list(itemno_list[sum_x:(sum_x+df_pv_list[i])])))
#每批次订单所含货品种类数列表
def getMax_per_batch_info(n):
    if df_pv_list == []:
        print('df_pv_list为空,数据已全部处理。')
        if orderno_list == []:
            print('orderno_list为空,订单已全部处理。')
            return

    #获取货品种类数最大的值
    if batch_s == []:
        max_values = max(df_pv_list)
        #df_pv_list.remove(max_values)
        # #获取货品种类数最大的值的索引
        index_values = df_pv_list.index(max_values)
        # #获取订单编号
        orderno_max = orderno_list[index_values]
        #将订单编号放入批次列表中
        batch_list.append(orderno_max)
        # #从dic中获取订单对应的货品
        item_list_max = eval(dic[orderno_max])
        #将货品加入每批次订单所含货品种类数列表
        for i in item_list_max:
            if i in batch_s:
                n=n
            else:
                batch_s.append(i)
                n=n+1
        df_pv_list.pop(index_values)
        orderno_list.remove(orderno_max)

    else:
        n = n
    return [batch_s,n]
def get_num_com(i_l_m):
    for x in orderno_list:
        count = 0
        for j in eval(dic[x]):
            for i in i_l_m:
                if j == i:
                    count = count + 1
                    break
        num_com.append(count)
    return num_com

def get_batch_info(n,num_com):
    while n < N:
        max_num_com = max(num_com)
        #获取最大重叠率的索引
        max_num_com_index = num_com.index(max_num_com)
        #获取最大相同种类数的的订单编号
        orderno_m = orderno_list[max_num_com_index]
        num = len(eval(dic[orderno_m]))
        #非重叠货品种类数量
        num_com_num = num_com[max_num_com_index]
        num_none = num - num_com[max_num_com_index]
        #批次总的货品种类数(不能大于200)
        n = n + num_none
        #print(n)
        while n > N:
            #print('n>N')
            orderno_list_copy = orderno_list[:]
            while True:
                n = n - num_none
                orderno_list_copy.remove(orderno_m)
                num_com.pop(max_num_com_index)
                if num_com == []:
                    break
                max_num_com = max(num_com)
                max_num_com_index = num_com.index(max_num_com)
                orderno_m = orderno_list_copy[max_num_com_index]
                num = len(eval(dic[orderno_m]))
                num_com_num = num_com[max_num_com_index]
                num_none = num - num_com[max_num_com_index]
                n = n + num_none
                if n>N:
                    orderno_list_copy = orderno_list_copy[:]
                    continue
                else:
                    break
            break
        if num_com == []:
            break
        else:
            # 将货品加入列表中
            for i in eval(dic[orderno_m]):
                if i in batch_s:
                    continue
                else:
                    batch_s.append(i)
            # 将订单编号放入批次列表中
            batch_list.append(orderno_m)

            df_pv_list.pop(max_num_com_index)
            orderno_list.remove(orderno_m)
            break
    return n




#主程序
sum_batch_s =[]
# 相同货品种类数列表
num_com  = []
#批次列表
batch_list = []
#总的批次列表
sum_batch_list = []
N = 200

for i in range(1,80):
    if df_pv_list == []:
        if orderno_list == []:
            break
    # 每批次订单所含货品种类数列表
    batch_list = []
    num_com = []
    batch_s = []
    n=0
    arg_list = getMax_per_batch_info(n)
    if arg_list == None:
        print('数据已全部处理。')
        break
    else:
        b=get_num_com(arg_list[0])
        n = get_batch_info(arg_list[1],b)



    while True:
        num_com = []
        arg_list = getMax_per_batch_info(n)
        if arg_list == None:
            sum_batch_list.append(batch_list)
            sum_batch_s.append(batch_s)
            print('数据已全部处理。')
            break
        else:
            b = get_num_com(arg_list[0])
            n = get_batch_info(arg_list[1], b)
            if n < N:
                continue
            else:
                sum_batch_list.append(batch_list)
                sum_batch_s.append(batch_s)
                break
print('正在统计最终结果,请稍等片刻。。。。。。。。。。')

OrderNo_list = []
GroupNo_list = []
for i in range(0,51):
    for j in sum_batch_list[i]:
        OrderNo_list.append(j)
        GroupNo_list.append(i+1)


#保存数据
result1_dic = {'OrderNo':OrderNo_list,
               'GroupNo':GroupNo_list}
result1_df = pd.DataFrame(result1_dic,index=None)
result1_df.reset_index(drop=True)
result1_df.to_csv(r'D:\Pycharm\pythonProject\result1.csv')

print('第一问给出结果如下:')

for i in range(51):
    print('第{}批订单数为:'.format(i+1),len(sum_batch_list[i]),'货品种类数为:',len(sum_batch_s[i]))
    print('分批方案为:',sum_batch_list[i])
    print('----------------------------------------------------------------')
    print()

print('总批数:',len(sum_batch_s))
print()
print('result1.csv文件已生成')
print('程序到此结束')

部分运行结果:

第1批订单数为: 22 货品种类数为: 200
分批方案为: ['D0148', 'D0613', 'D0674', 'D0246', 'D0726', 'D0337', 'D0497', 'D0581', 'D0601', 'D0391', 'D0352', 'D0630', 'D0747', 'D0912', 'D0294', 'D0496', 'D0423', 'D0539', 'D0776', 'D0384', 'D0903', 'D0210']
—————————————————————-

第2批订单数为: 8 货品种类数为: 200
分批方案为: ['D0189', 'D0233', 'D0149', 'D0164', 'D0211', 'D0270', 'D0036', 'D0106']
—————————————————————-

第3批订单数为: 20 货品种类数为: 200
分批方案为: ['D0174', 'D0089', 'D0218', 'D0580', 'D0466', 'D0426', 'D0281', 'D0443', 'D0403', 'D0513', 'D0375', 'D0784', 'D0868', 'D0906', 'D0488', 'D0764', 'D0861', 'D0490', 'D0234', 'D0646']
—————————————————————-

第4批订单数为: 20 货品种类数为: 200
分批方案为: ['D0145', 'D0409', 'D0468', 'D0190', 'D0320', 'D0670', 'D0505', 'D0882', 'D0474', 'D0413', 'D0591', 'D0540', 'D0498', 'D0572', 'D0825', 'D0486', 'D0612', 'D0433', 'D0401', 'D0116']
—————————————————————-

第5批订单数为: 10 货品种类数为: 200
分批方案为: ['D0203', 'D0708', 'D0412', 'D0187', 'D0769', 'D0117', 'D0717', 'D0232', 'D0766', 'D0102']
—————————————————————-

第6批订单数为: 13 货品种类数为: 200
分批方案为: ['D0028', 'D0046', 'D0707', 'D0815', 'D0259', 'D0898', 'D0870', 'D0173', 'D0705', 'D0219', 'D0178', 'D0381', 'D0354']
—————————————————————-

第7批订单数为: 6 货品种类数为: 200
分批方案为: ['D0283', 'D0458', 'D0006', 'D0744', 'D0553', 'D0063']
—————————————————————-

第8批订单数为: 30 货品种类数为: 200
分批方案为: ['D0133', 'D0767', 'D0481', 'D0741', 'D0060', 'D0194', 'D0678', 'D0340', 'D0142', 'D0147', 'D0397', 'D0792', 'D0891', 'D0894', 'D0475', 'D0899', 'D0386', 'D0851', 'D0830', 'D0399', 'D0878', 'D0166', 'D0460', 'D0544', 'D0042', 'D0019', 'D0043', 'D0550', 'D0800', 'D0078']
—————————————————————-
 

注意:代码里面的data.csv文件为官网上题目里的附带文件,我改成了data,csv名字;OrderNoSum.csv文件是经过data.csv文件处理的文件,将它与data.csv放在同一目录下。

第1问至第3问相关文件和源码放这了:

链接:https://pan.baidu.com/s/11UBm1LPLgAANu-nixi9KEQ 
提取码:4859

答案不一定准确,有错误或者不恰当的地方请指出!一起学习进步!!

来源:han_liang09

物联沃分享整理
物联沃-IOTWORD物联网 » 第十四届华中杯大学生数学建模挑战赛A题Python解答共51批(附文件源码链接)需者自取

发表评论