求类Split Wise群组支出余额计算算法及Python实现
群组支出余额计算算法(Python实现)
核心思路
要计算用户间的余额,需分两步处理每个支出项,再累加所有支出的结果:
- 单支出项计算:
- 计算该支出的人均应摊金额
- 找出多付(实际支付>应摊)和少付(实际支付<应摊)的用户
- 少付用户按比例将欠款分给多付用户
- 总余额累加:将所有支出项中用户间的欠款金额累加,得到最终的互相欠款关系
Python代码实现
def calculate_balance(group_expenses): # 初始化余额字典,每个用户对应一个空字典记录对其他用户的欠款 users = group_expenses[0].keys() if group_expenses else [] balance = {user: {other: 0.0 for other in users if other != user} for user in users} for expense in group_expenses: # 计算当前支出的总额和人均应摊金额 total_expense = sum(expense.values()) num_users = len(expense) if num_users == 0: continue per_person = total_expense / num_users # 计算每个用户的收支差额:实际支付 - 应摊金额(正=多付,负=少付) diffs = {user: paid - per_person for user, paid in expense.items()} # 筛选多付和少付的用户,少付金额取正数表示需要补的钱 overpaid = {user: diff for user, diff in diffs.items() if diff > 1e-9} # 避免浮点误差 underpaid = {user: -diff for user, diff in diffs.items() if diff < -1e-9} total_over = sum(overpaid.values()) if total_over < 1e-9: # 该支出已平摊,无需处理 continue # 分配少付用户的欠款给多付用户 for under_user, under_amount in underpaid.items(): for over_user, over_amount in overpaid.items(): # 计算当前少付用户需要给多付用户的金额 amount = under_amount * (over_amount / total_over) # 更新余额:under_user 欠 over_user 的金额增加 balance[under_user][over_user] += amount # 反向更新:over_user 欠 under_user 的金额减少(即 under_user 欠 over_user 正数,over_user 欠 under_user 负数) balance[over_user][under_user] -= amount # 处理浮点精度问题,保留两位小数(可选) for user in balance: for other in balance[user]: balance[user][other] = round(balance[user][other], 2) return balance # 测试示例数据 group_expenses = [ {"John": 0, "Mary": 0, "Maya": 30}, {"John": 60, "Mary": 0, "Maya": 0} ] balance = calculate_balance(group_expenses) print("John's balance:", balance["John"]) # {'Mary': -20.0, 'Maya': -10.0} print("Mary's balance:", balance["Mary"]) # {'John': 20.0, 'Maya': 10.0} print("Maya's balance:", balance["Maya"]) # {'John': 10.0, 'Mary': -10.0}
代码说明
- 初始化余额字典:从第一个支出项提取所有用户,为每个用户创建对应其他用户的欠款记录,初始值为0。
- 遍历每个支出项:
- 计算该支出的总额和人均应摊金额。
- 计算每个用户的收支差额,区分多付和少付用户。
- 按比例将少付用户的欠款分配给多付用户,更新双向余额记录(比如A欠B10元,等价于B欠A-10元)。
- 浮点精度处理:使用
round保留两位小数,避免浮点运算带来的微小误差。
验证结果
运行测试代码后,输出与示例预期完全一致:
- John的余额显示Mary欠他20元,Maya欠他10元(负数表示对方欠自己)。
- Mary的余额显示她欠John20元、欠Maya10元。
- Maya的余额显示她欠John10元,Mary欠她10元。
内容的提问来源于stack exchange,提问作者kiryha
相关产品推荐
相关产品推荐

