有一些金额不等的合同需要分配给n个员工,尽可能地实现员工分配到的金额和数量是平均的。
网上找到了实现的算法,原文平均分配,移动欠费催收款数据的分配应用实例 - 李振波 - 博客园
里面给出了三种可行算法
第一种:
倒序贪婪(我称之为倒序贪婪,也不懂该叫什么)
过程大概是这样(把m份数据分配到n个人的头上)
1.1 把待分配的数据m从大到小排序;
1.2 从数据m取出n份做为初始值分配给n个人;
1.3 把这n个人的数据从小到大排序;
1.4 从数据m再取出n份数据累加到n个人的头上
1.5 重复1.3-1.4直至数据分配结束
第二种:
随机贪婪(我称之为随机贪婪,也不懂该叫什么)
过程大概是这样(把m份数据分配到n个人的头上)
1.1 从数据m中随机取出n份做为初始值分配给n个人;
1.2 把n个人的数据从小到大排序;
1.3 从数据m中随机再取出n份数据累加到n个人的头上
1.4 重复1.2-1.3直至数据分配结束
第三种:
先进行几轮倒序贪婪,把大额度的数据排除,余下的数据再进行随机贪婪
该文章作者用asp实现了,并且作者说第三种方法效果最好。我用python实现了,但是我的数据是是第一种方法效果最好,还没找到原因,可能是我的数据量大,没有很多离群的点?目前还不知道原因。
我的python代码
1.先倒序再贪婪
import pymysql import pandas as pd import numpy as np def excuteSQL(excutesql): db = pymysql.connect(host='localhost', user='root', password='admin',database='cuishou') cursor = db.cursor() cursor.execute(excutesql) result = cursor.fetchall() title = [i[0].lower() for i in cursor.description] print('数据获取完毕!') cursor.close() db.close() return result, title def commitSQL(excutedata): db = pymysql.connect(host='localhost', user='root', password='admin', database='cuishou') cursor = db.cursor() cursor.executemany("insert into fenpei(uid,cid,camt) values(%s,%s,%s)", excutedata) db.commit() cursor.close() db.close() print('数据插入完毕!') if __name__=='__main__': # 取出金额数据 result,title = excuteSQL('select * from calllist;') repay_data = pd.DataFrame(list(result),columns=title) # 取出员工数据 result,title = excuteSQL('select * from user;') user_data = pd.DataFrame(list(result),columns=title) # 第一步:打乱员工顺序 user_data = user_data.sample(frac=1).reset_index(drop=True) user_num=user_data.shape[0] # 第二步:案件金额倒序排序 repay_data = repay_data.sort_values(by='repay',ascending=False) repay_data = repay_data.reset_index(drop=True) # repay_data = repay_data[:7] repay_num=repay_data.shape[0] # 第三步:如果案件数量少于员工数量,直接分配 if repay_num <= user_num: fenpei_data=pd.DataFrame({'uid': user_data.iloc[:repay_num, 0], 'cid': repay_data.iloc[:, 0], 'camt': repay_data.iloc[:, 1]}) # 第四步:先倒序分配,当分配案件数超过总案件数量的五分之一时,开始随机分配 else: # 先分配一轮 fenpei_data=pd.DataFrame({'uid': user_data.iloc[:, 0], 'cid': repay_data.iloc[:user_num, 0], 'camt': repay_data.iloc[:user_num, 1]}) i=user_num # 倒序分配 while i < (repay_num//2): qiuhe_data = fenpei_data.groupby('uid')['camt'].sum().sort_values() linshi_data = pd.DataFrame({'uid': qiuhe_data.index, 'cid': repay_data.iloc[i:i+user_num, 0], 'camt': repay_data.iloc[i:i+user_num, 1] }) fenpei_data = pd.concat([fenpei_data, linshi_data]) i = i+user_num # 将案件随机打乱 repay_data = repay_data[i:].sample(frac=1).reset_index(drop=True) repay_num_new=repay_data.shape[0] i=0 while i < repay_num_new: # 取user_num和repay_num_new-i之间的较小值 赋值给j j = user_num if user_num < (repay_num_new-i) else (repay_num_new-i) # user现有金额升序排列 qiuhe_data = fenpei_data.groupby('uid')['camt'].sum().sort_values() qiuhe_data = qiuhe_data.iloc[:j] # 取出一批案件倒序排列 linshi_repay = repay_data[i:i+j].sort_values(by='repay', ascending=False) # 加入到分配数据中 linshi_data = pd.DataFrame({'uid': qiuhe_data.index, 'cid': linshi_repay['id'], 'camt': linshi_repay['repay']}) fenpei_data = pd.concat([fenpei_data, linshi_data]) i = i+j result_tuple=[tuple(xi) for xi in fenpei_data.values] commitSQL(result_tuple)
2.全倒序
# 第三步:如果案件数量少于员工数量,直接分配 if repay_num <= user_num: fenpei_data=pd.DataFrame({'uid': user_data.iloc[:repay_num, 0], 'cid': repay_data.iloc[:, 0], 'camt': repay_data.iloc[:, 1]}) else: # 先分配一轮 fenpei_data=pd.DataFrame({'uid': user_data.iloc[:, 0], 'cid': repay_data.iloc[:user_num, 0], 'camt': repay_data.iloc[:user_num, 1]}) i=user_num while i < repay_num: # 取user_num和repay_num_new-i之间的较小值 赋值给j j = user_num if user_num < (repay_num-i) else (repay_num-i) # user现有金额升序排列 qiuhe_data = fenpei_data.groupby('uid')['camt'].sum().sort_values() qiuhe_data = qiuhe_data.iloc[:j] # 取出一批案件倒序排列 linshi_repay = repay_data[i:i+j].sort_values(by='repay', ascending=False) # 加入到分配数据中 linshi_data = pd.DataFrame({'uid': qiuhe_data.index, 'cid': linshi_repay['id'], 'camt': linshi_repay['repay']}) fenpei_data = pd.concat([fenpei_data, linshi_data]) i = i+j
3.全贪婪
# 第三步:如果案件数量少于员工数量,直接分配 if repay_num <= user_num: fenpei_data=pd.DataFrame({'uid': user_data.iloc[:repay_num, 0], 'cid': repay_data.iloc[:, 0], 'camt': repay_data.iloc[:, 1]}) else: # 先分配一轮 fenpei_data=pd.DataFrame({'uid': user_data.iloc[:, 0], 'cid': repay_data.iloc[:user_num, 0], 'camt': repay_data.iloc[:user_num, 1]}) i=user_num # 倒序分配 while i < repay_num: j = user_num if user_num < (repay_num-i) else (repay_num-i) qiuhe_data = fenpei_data.groupby('uid')['camt'].sum().sort_values() qiuhe_data = qiuhe_data.iloc[:j] linshi_repay = repay_data[i:i + j].sort_values(by='repay', ascending=False) # 加入到分配数据中 linshi_data = pd.DataFrame({'uid': qiuhe_data.index, 'cid': linshi_repay['id'], 'camt': linshi_repay['repay']}) fenpei_data = pd.concat([fenpei_data, linshi_data]) i = i + j