Wire-Min-Splitter
Enter party spending and optional fairness adjustments, then generate minimal transfer instructions.
Developer: @ysBach (Github)
| Name | Repeat | Spent | Extra Pay | Extra Get | Preferred Multi-Payer |
|---|
Result
Net Balances
Transfers
Original Plan (Before Multiplier)
Net Balances
Transfers
Multiplied Plan (After Multiplier)
Net Balances
Transfers
How the algorithm works
This tool finds the fewest direct payer-to-receiver transfers for balances rounded to cents. It computes with exact fractions. The code runs in your browser. It works offline after your browser caches the app.
1. Read the input
- Names: Unicode NFC, trimmed, cut to 120 code points. Each name must be non-empty and unique.
- Read money as exact BigInt fractions. The solver uses no floating-point money.
- Each spent value is 0 or more. The multiplier m must be above 0. Total Extra Get must equal total Extra Pay exactly.
- A Repeat row with n above 1 becomes n people with numbered names (for example, Team 01, Team 02). A row with n = 1 keeps the typed name.
2. Compute the cent balances
N = number of people T = sum of spent m = multiplier b_i = 100 · m · (spent_i − T/N + get_i − pay_i) spent = Spent, get = Extra Get, pay = Extra Pay b_i = ideal balance in cents (exact fraction)
- Compute each b_i as an exact fraction.
- Round every b_i down to a whole cent (floor: the largest integer not above b_i). Example: −33⅓ becomes −34.
- The sum is now 0 or below. It is short of 0 by fewer than N cents. Call the shortage k.
- Add 1 cent to the k people with the largest fractional remainders. Break ties by name, in Unicode code point order.
- Positive balance: the person receives. Negative: the person pays. Zero: removed from the search.
Each cent balance differs from its ideal value by less than 1 cent. The sum is exactly 0. This largest-remainder allocation preserves the total. Identical participants can differ by 1 cent.
Example
A paid 1.00. B and C paid 0. Multiplier 1. Ideal cents: A +66⅔, B −33⅓, C −33⅓ Round down: 66, −34, −34 → sum −2, so k = 2 Remainders: 2/3, 2/3, 2/3 (tie) Tie by name: A and B get +1 cent Balances: A +0.67, B −0.33, C −0.34 (sum 0) Transfers: B → A 0.33, C → A 0.34
3. Show the views
- Multiplier 1: one plan.
- Multiplier other than 1: the Multiplied plan uses m. The Original plan uses factor 1.
- The solver calculates each view independently. In strict mode, one view can show a no-plan error while the other shows a plan.
- The rounding controls change only the shown numbers. Units other than 0.01 are approximate. Use 0.01 for exact payment instructions.
4. Set the goal
minimize (transfers, burden) in lexicographic order: fewest transfers first, then smallest burden burden = Σ max(0, out_i − 1), summed over non-preferred payers i out_i = number of transfers that payer i sends
- Preferred payers (Preferred Multi-Payer) have no penalty.
- Strict mode: each non-preferred payer sends at most 1 transfer. Receivers have no limit.
- If strict mode allows no plan, the result is a no-plan error.
5. Search the plans
state = payers [(amount, preferred, has_sent)] + receiver amounts has_sent = 1: the payer already sent a transfer in this plan move = payer i pays receiver j an amount of min(a_i, r_j) best(state) = min_lex over all moves of (1 + child.count, extra + child.penalty) extra = 1 if the payer has_sent and is non-preferred, else 0 best(empty state) = (0, 0) Skip infeasible children. Strict mode: skip moves with extra = 1. min_lex = smallest pair, compared by count first, then penalty
- Run an iterative depth-first search. Try every distinct payer/receiver pair. Move the smaller of the two remaining amounts. Each move removes at least 1 person from the state.
- Payers are equal only if amount, preferred flag, and has_sent flag all match. Receivers are equal if their amounts match. The search tries equal choices once.
- The search caches each state as sorted payer tuples and sorted receiver amounts.
- One payer left, or one receiver left: an exact closed form gives the answer. It follows the strict rule.
- Lower bound: max(payers left, receivers left), computed after the proposed move. Skip the move only if 1 + bound is more than the best count found. Equal counts stay, so burden can still decide.
If plans tie, the search uses canonical amounts, payer flags and has_sent status, then normalized names to pick one plan in a fixed way.
6. Why the result is exact
- Take any plan that has a cycle (ignore the arrow directions). Shift +δ and −δ alternately along the cycle edges. Every participant's balance stays the same. Stop when one transfer is 0.
- This creates no new payer/receiver pair. It raises no payer's outgoing count. So the transfer count, the burden, and the strict rule are never worse.
- So some optimal plan has no cycle. It is a forest.
- A non-empty forest has a leaf: a person with 1 transfer. That transfer carries the leaf's whole remaining amount. This amount is the smaller remaining amount of the pair.
- The search tries every such pair, then repeats on the rest. So it reaches an optimal plan.
Scope: the plan is optimal among direct payer-to-receiver plans for the cent balances. This tool makes no claim for other kinds of plan. Other plans can tie.
7. Know the limits
- Run time can grow exponentially with the number of payers and receivers.
- The search runs in a local worker in your browser. It works offline after your browser caches the app code.
- The solver adds no timeout and no limit on the number of people. Use Cancel to stop a long search.
- Cancel, or a memory or stack failure, is not a no-plan result. It says nothing about feasibility.
- Only a successfully completed search returns an optimum for the rounded balances.