Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

split.gno

2.07 Kb · 87 lines
 1package ledger
 2
 3// Equal splits total into n parts that differ by at most one cent; the first
 4// total%n parts take the extra cent. A negative total (money received) splits
 5// the same way, every part negative.
 6func Equal(total int64, n int) []int64 {
 7	w := make([]int64, n)
 8	for i := range w {
 9		w[i] = 1
10	}
11	return Weighted(total, w)
12}
13
14// Weighted splits total in proportion to weights, all positive, by largest
15// remainder: each part is rounded down, then the cents left go to the largest
16// remainders, ties to the first. The parts always sum to total.
17func Weighted(total int64, weights []int64) []int64 {
18	neg := total < 0
19	if neg {
20		total = -total
21	}
22	var sum int64
23	for _, w := range weights {
24		sum += w
25	}
26	parts := make([]int64, len(weights))
27	rem := make([]int64, len(weights))
28	left := total
29	for i, w := range weights {
30		parts[i] = total * w / sum
31		rem[i] = total * w % sum
32		left -= parts[i]
33	}
34	for ; left > 0; left-- {
35		best := 0
36		for i := 1; i < len(rem); i++ {
37			if rem[i] > rem[best] {
38				best = i
39			}
40		}
41		parts[best]++
42		rem[best] = -1 // one extra cent at most per part
43	}
44	if neg {
45		for i := range parts {
46			parts[i] = -parts[i]
47		}
48	}
49	return parts
50}
51
52// Transfer is one payment that settles part of a group: From pays To.
53type Transfer struct {
54	From, To int
55	Amount   int64
56}
57
58// Settle returns the transfers that bring every balance to zero (balances
59// sum to zero; > 0 means the group owes that member). The largest debt pays
60// the largest credit, ties to the lower index, so each step clears at least
61// one member: at most n-1 transfers, in O(n²) for the few members of a group.
62func Settle(balances []int64) []Transfer {
63	bal := make([]int64, len(balances))
64	copy(bal, balances)
65	var out []Transfer
66	for {
67		cr, dr := -1, -1
68		for i, b := range bal {
69			if b > 0 && (cr < 0 || b > bal[cr]) {
70				cr = i
71			}
72			if b < 0 && (dr < 0 || b < bal[dr]) {
73				dr = i
74			}
75		}
76		if cr < 0 || dr < 0 {
77			return out
78		}
79		amt := bal[cr]
80		if -bal[dr] < amt {
81			amt = -bal[dr]
82		}
83		out = append(out, Transfer{From: dr, To: cr, Amount: amt})
84		bal[cr] -= amt
85		bal[dr] += amt
86	}
87}