This is kind of more generic question, isn\'t language-specific. More about idea and algorithm to use.
The system is as follows:
It registers small loans bet
This is the code I wrote to solve this type of a problem in Java. I am not 100% sure if this gives the least number of transactions. The code's clarity and structure can be improved a lot.
In this example:
John spent $100 on groceries. The groceries were consumed by Bob and Alice.
import java.util.*;
public class MoneyMinTransactions {
static class Expense{
String spender;
double amount;
List consumers;
public Expense(String spender, double amount, String... consumers){
this.spender = spender;
this.amount = amount;
this.consumers = Arrays.asList(consumers);
}
}
static class Owed{
String name;
double amount;
public Owed(String name, double amount){
this.name = name;
this.amount = amount;
}
}
public static void main(String[] args){
List expenseList = new ArrayList<>();
expenseList.add(new Expense("Sarah", 400, "Sarah", "John", "Bob", "Alice"));
expenseList.add(new Expense("John", 100, "Bob", "Alice"));
//make list of who owes how much.
Map owes = new HashMap<>();
for(Expense e:expenseList){
double owedAmt = e.amount/e.consumers.size();
for(String c : e.consumers){
if(!e.spender.equals(c)){
if(owes.containsKey(c)){
owes.put(c, owes.get(c) + owedAmt);
}else{
owes.put(c, owedAmt);
}
if(owes.containsKey(e.spender)){
owes.put(e.spender, owes.get(e.spender) + (-1 * owedAmt));
}else{
owes.put(e.spender, (-1 * owedAmt));
}
}
}
}
//make transactions.
// We need to settle all the negatives with positives. Make list of negatives. Order highest owed (i.e. the lowest negative) to least owed amount.
List owed = new ArrayList<>();
for(String s : owes.keySet()){
if(owes.get(s) < 0){
owed.add(new Owed(s, owes.get(s)));
}
}
Collections.sort(owed, new Comparator() {
@Override
public int compare(Owed o1, Owed o2) {
return Double.compare(o1.amount, o2.amount);
}
});
//take the highest negative, settle it with the best positive match:
// 1. a positive that is equal to teh absolute negative amount is the best match.
// 2. the greatest positive value is the next best match.
// todo not sure if this matching strategy gives the least number of transactions.
for(Owed owedPerson: owed){
while(owes.get(owedPerson.name) != 0){
double negAmt = owes.get(owedPerson.name);
//get the best person to settle with
String s = getBestMatch(negAmt, owes);
double posAmt = owes.get(s);
if(posAmt > Math.abs(negAmt)){
owes.put(owedPerson.name, 0.0);
owes.put(s, posAmt - Math.abs(negAmt));
System.out.println(String.format("%s paid %s to %s", s, Double.toString((posAmt - Math.abs(negAmt))), owedPerson.name));
}else{
owes.put(owedPerson.name, -1 * (Math.abs(negAmt) - posAmt));
owes.put(s, 0.0);
System.out.println(String.format("%s paid %s to %s", s, Double.toString(posAmt), owedPerson.name));
}
}
}
}
private static String getBestMatch(double negAmount, Map owes){
String greatestS = null;
double greatestAmt = -1;
for(String s: owes.keySet()){
double amt = owes.get(s);
if(amt > 0){
if(amt == Math.abs(negAmount)){
return s;
}else if(greatestS == null || amt > greatestAmt){
greatestAmt = amt;
greatestS = s;
}
}
}
return greatestS;
}
}