Here is a handy script that uses the simplex algorithm to compute an optimum list of refunds (for example, after a trip with shared expenses with friends).
It minimizes the number of transactions (refunds) required to reach the balance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #!/usr/bin/python
#
# This script takes dues in input (std input) and compute an optimum
# list of refunds.
# Format of input list (a negative is a due)
# John -10
# Jack +20
# Jessie -
#
# If the sum is not equal to zero, the algorithm never stops.
import sys
# CONSTANTS
BALANCE_THRESHOLD = 0.02
# Init map of dues
dues = {}
# Read std in
for line in sys.stdin :
# Split into two parts
(name, due) = line.strip().split()
# Fill the map of dues
dues[name] = float(due)
# Init list of refunds (from, to, sum)
refunds = []
# Loop until the balance is reached
while True :
# Get the min/max due
maxName = max(dues, key=dues.get)
minName = min(dues, key=dues.get)
# Min == Max ?? => exit
if minName == maxName : break
# Get dues
minDue = dues[minName]
maxDue = dues[maxName]
# What can we exchange ?
maxRefund = min(abs(minDue), abs(maxDue))
# Balance reached ?
if maxRefund <= BALANCE_THRESHOLD : break
# Add a refund
refunds.append((minName, maxName, maxRefund))
# Update dues
dues[minName] += maxRefund
dues[maxName] -= maxRefund
# Print refunds
for (fromName, toName, refund) in refunds :
print "%s => %s : %.2f" % (fromName, toName, refund)
|
This script takes a list of dues in standard input in this format
Gerald -36.56
Remi -36.56
Marco -237.97
Marion -102.47
Raphael -96.11
Nicolas -23.47
Francois +29.83
Magali +118.32
Anthony +385.01
The negative amount are dues. The sum should be equal to zero, for the script to ends correctly.
You may call this script with ''cat'':
cat dues.txt | balance.py
The output looks like this :
Marco => Anthony : 237.97
Marion => Anthony : 102.47
Raphael => Magali : 96.11
Gerald => Anthony : 36.56
Remi => Francois : 29.83
Nicolas => Magali : 22.21
Remi => Anthony : 6.73
Nicolas => Anthony : 1.26