金额平均分配算法 python实现

有一些金额不等的合同需要分配给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
上一篇:SQLiteOpenHelper数据库的创建or升级or增删改查,kotlin开发windows程序


下一篇:使用上下文管理器来简化sql提交及异常操作