commit cdca6df82121ada4d864b2cbaa1b9fe5cb1a36bc Author: Nikolai Hartmann Date: Tue Sep 19 07:02:45 2023 +0200 initial commit diff --git a/splitbill.py b/splitbill.py new file mode 100755 index 0000000..4284f52 --- /dev/null +++ b/splitbill.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +from itertools import chain, combinations +from typing import Generator + +balances = { + # "A": -20, + # "B": 10, + # "C": 3, + # "D": -43, + # should be possible with 3 transactions (A, B, C balance excactly) + "A": 50, + "B": -30, + "C": -20, + "D": -40, +} +balances["E"] = -sum(balances.values()) + + +def find_zerosum_subgroups( + balances: dict[str, int], +) -> Generator[tuple[str, ...], None, None]: + for n in range(2, len(balances)): + for combination in combinations(balances, n): + if sum(balances[key] for key in combination) == 0: + yield combination + + +def solve_greedily(balances: dict[str, int]) -> dict[tuple[str, str], int]: + creditors = {} + debitors = {} + for k, v in balances.items(): + if v > 0: + creditors[k] = v + else: + debitors[k] = v + + txn = {} + while not all(value == 0 for value in chain(creditors.values(), debitors.values())): + for debitor, debit_value in sorted(debitors.items(), key=lambda x: x[1]): + for creditor, credit_value in sorted( + creditors.items(), key=lambda x: x[1], reverse=True + ): + sum_value = credit_value + debit_value + if abs(debit_value) <= credit_value: + del debitors[debitor] + creditors[creditor] = sum_value + txn[debitor, creditor] = abs(debit_value) + else: + debitors[debitor] = sum_value + del creditors[creditor] + txn[debitor, creditor] = credit_value + break + + return txn + + +def solve(balances: dict[str, int]) -> dict[tuple[str, str], int]: + possibilities = [] + for subgroup in find_zerosum_subgroups(balances): + txn_sub = solve({k: balances[k] for k in subgroup}) + txn_other = solve({k: balances[k] for k in balances if not k in subgroup}) + possibilities.append(txn_sub | txn_other) + if not possibilities: + possibilities.append(solve_greedily(balances)) + return min(possibilities, key=lambda x: len(x)) + + +print(solve(balances))