Last active
June 23, 2025 15:55
-
-
Save CoryMcCartan/7072a9567465c1dfdc49cf6b34e59fcc to your computer and use it in GitHub Desktop.
Find a sparse set of payments to settle expenses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # vector of net paid/owed, i.e., positive means owed money | |
| b = c(Alice=-15, Bob=130, Charlie=-125, Diane=150, Ed=20, Frank=-160) | |
| stopifnot(sum(b) == 0) | |
| n = length(b) | |
| m = n * (n - 1) / 2 | |
| A = combn(n, 2, function(x) { | |
| out = numeric(n) | |
| out[x] = c(1, -1) | |
| out | |
| }) | |
| # basis pursuit | |
| rlang::check_installed("lpSolve") | |
| res = lpSolve::lp( | |
| objective.in = rep(1, 2*m), | |
| const.mat = rbind(cbind(A, -A), diag(2*m)), | |
| const.dir = c(rep("==", n), rep(">=", 2*m)), | |
| const.rhs = c(b, rep(0, 2*m)), | |
| transpose.constraints = TRUE | |
| ) | |
| x = res$solution | |
| payers = c(max.col(t(A) < 0)[x[1:m] > 0], max.col(t(A) > 0)[x[m + 1:m] > 0]) | |
| payees = c(max.col(t(A) > 0)[x[1:m] > 0], max.col(t(A) < 0)[x[m + 1:m] > 0]) | |
| ord = order(payers, payees) | |
| nm = names(b) %||% seq_along(b) | |
| data.frame(payer = nm[payers[ord]], payee = nm[payees[ord]], amount = x[x > 0][ord]) | |
| #> payer payee amount | |
| #> 1 Alice Bob 5 | |
| #> 2 Alice Diane 10 | |
| #> 3 Charlie Bob 125 | |
| #> 4 Frank Diane 140 | |
| #> 5 Frank Ed 20 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment