TL;DR — Once you split something among people — bandwidth, budget, or satisfaction with a shared choice — you need a number for how fairly it was split. Four complementary measures do this: min (the worst-off share — a floor), the Jain index (
(Σxᵢ)² / (n·Σxᵢ²), from1/nup to1where1is perfectly equal), the Gini coefficient (0equal, toward1maximally unequal), and max-envy (the most any person would gain by taking someone else's share;0is envy-free). No single number is enough — a distribution can look equal by variance yet still be envious — so report a floor metric, a spread metric, and an envy metric together. They all assume the shares are comparable numbers; for rank-only or need-based fairness they mislead.
1. Simple explanation
Suppose you divide something among several people and want to say, in one number, whether the split was fair. "Fair" turns out to mean several different things, and they don't always agree:
- Is anyone left with almost nothing? — look at the smallest share (the floor).
- How spread out are the shares overall? — look at a spread measure (Jain or Gini).
- Would anyone rather have someone else's share? — look at envy.
A split can score well on one and badly on another. Four people each getting 0.6 is perfectly equal (great spread, no envy, decent floor). One person getting everything is terrible on all three. But the interesting cases are in between, where the measures disagree — and that disagreement is exactly the information you want.
Analogy — cutting one cake for four kids. Equal slices: nobody complains (fair by every measure). Now give three kids big slices and one kid a crumb: the average slice is still fine, but the smallest slice is tiny (bad floor), the slices are lopsided (bad spread), and the crumb kid stares at the others' plates (high envy). A parent doesn't check the average slice — they check the smallest slice and whether anyone is eyeing a neighbor's plate. Those are the floor and envy metrics.
2. Diagram
Three splits of "satisfaction" among 4 people, scored by each metric:
split shares min Jain Gini verdict
---------- ---------------- ---- ----- ----- ------------------------
equal .6 .6 .6 .6 .60 1.000 0.000 perfectly fair
skewed .9 .9 .9 .1 .10 0.803 0.214 one person stranded
one-hogs .9 0 0 0 .00 0.250 0.750 worst case (Jain -> 1/n)
Reading the scales:
min : 0 .......................... higher = better floor
Jain : 1/n ....................... 1 higher = more equal (1 = equal)
Gini : 0 ....................... ~1 lower = more equal (0 = equal)
Note Jain and Gini move in OPPOSITE directions. Do not confuse them.
3. How it works
3.1 The floor: minimum share
The simplest fairness signal is the smallest value, min(x). It answers "how bad is it for the worst-off person?" and nothing else. It is the metric behind the Least-Misery decision rule and behind service-level guarantees ("no user gets less than X"). Its blindness is deliberate: it ignores everyone above the minimum, so two splits with the same floor look identical to it even if one is otherwise far more equal. Use it as a guardrail, not a summary.
3.2 A spread measure that rewards equality: the Jain index
Jain's fairness index (from network resource allocation) is:
Jain(x) = ( sum_i x_i )^2 / ( n * sum_i x_i^2 )
It ranges from 1/n (one person has everything) to 1 (everyone equal). Two properties make it popular: it is scale-free (doubling every share leaves it unchanged — it measures shape, not size) and population-independent (comparable across group sizes). A handy intuition: Jain ≈ 0.8 in a group of 5 behaves "as if" 4 of the 5 are treated fairly. It is closely related to variance — for a fixed mean, higher variance means lower Jain — but it is normalized to [1/n, 1], which makes it easier to report than a raw variance.
3.3 A spread measure from inequality economics: the Gini coefficient
Gini comes from income inequality. One equivalent definition is the mean absolute difference between all pairs, normalized:
Gini(x) = ( sum_i sum_j |x_i - x_j| ) / ( 2 * n^2 * mean(x) )
It is 0 when everyone is equal and approaches 1 as one person takes everything. Watch the direction: Gini and Jain point opposite ways — low Gini is good, high Jain is good. Reporting both is common because they emphasize different parts of the distribution: Gini is sensitive to differences across the whole range, while Jain (being tied to squared values) reacts strongly to a few large shares.
3.4 Envy: would I rather have your share?
Floor and spread only look at the numbers people received. Envy asks a relational question: given what everyone got, would person i prefer person j's share? Formally, with value[i][b] = person i's value for share b and person i receiving share alloc[i]:
envy_i = max_j value[i][alloc[j]] - value[i][alloc[i]]
max-envy = max_i envy_i (0 => envy-free)
Envy captures a fairness people actually feel and that spread metrics miss: an allocation can have low variance yet still be envious if people value the shares differently. Its cost is that it needs each person's valuation of every share, not just their own — n^2 comparisons — which is why floor/spread metrics are reported far more often even though envy is closer to the felt experience.
Boundary condition. All four assume shares are cardinal and comparable across people. If people can only rank outcomes, or if fairness should mean "serve the greatest need first," these numbers mislead — a low-Gini split can still be unjust when needs differ. Section 8 lists the substitutes.
4. The math
4.1 The formulas
For shares x = [x_1, ..., x_n] with mean m:
min min_i x_i
variance (1/n) * sum_i (x_i - m)^2
Jain ( sum_i x_i )^2 / ( n * sum_i x_i^2 ) in [1/n, 1]
Gini ( sum_i sum_j |x_i - x_j| ) / ( 2*n^2*m ) in [0, ~1)
max-envy max_i ( max_j value[i][alloc[j]] - value[i][alloc[i]] )
4.2 Worked example
Take three splits among four people and one small envy scenario.
Split [0.6, 0.6, 0.6, 0.6]: everyone equal, so min = 0.60, Jain = 1.000, Gini = 0.000. This is the reference point.
Split [0.9, 0.9, 0.9, 0.1]: the mean is a healthy 0.70, but the floor collapses to 0.10. Jain drops to 0.803 and Gini rises to 0.214 — both flag the lopsidedness that the mean hid. This is the "high average, one person stranded" pattern.
Split [0.9, 0, 0, 0]: one person has everything. Jain hits its worst possible value 1/n = 0.25, and Gini climbs to 0.750.
Envy: three people, three bundles, valuations value = [[10,8,3],[4,9,2],[6,5,7]], and allocation alloc = [1,0,2] (person 0 gets bundle 1, person 1 gets bundle 0, person 2 gets bundle 2). Person 0 holds a bundle worth 8 to them but sees bundle 0 worth 10 — envy 2. Person 1 holds 4 but sees 9 — envy 5. Person 2 holds their best (7) — envy 0. So max-envy = 5: this split is efficient-looking but far from envy-free.
5. Real code
# Four ways to measure how fairly a set of satisfactions (or resources) is shared.
def mean(x): return sum(x) / len(x)
def variance(x): m = mean(x); return sum((v - m) ** 2 for v in x) / len(x)
def jain(x): # 1/n .. 1 ; 1 = perfectly equal
return sum(x) ** 2 / (len(x) * sum(v * v for v in x))
def gini(x): # 0 = equal .. ->1 = maximally unequal
n, m = len(x), mean(x)
if m == 0: return 0.0
return sum(abs(a - b) for a in x for b in x) / (2 * n * n * m)
cases = {
"equal [.6,.6,.6,.6]": [0.6, 0.6, 0.6, 0.6],
"skewed [.9,.9,.9,.1]": [0.9, 0.9, 0.9, 0.1],
"one-hogs [.9, 0, 0, 0]": [0.9, 0.0, 0.0, 0.0],
}
print(f"{'case':<25} {'min':>5} {'mean':>6} {'var':>6} {'Jain':>6} {'Gini':>6}")
for name, x in cases.items():
print(f"{name:<25} {min(x):>5.2f} {mean(x):>6.3f} {variance(x):>6.3f} {jain(x):>6.3f} {gini(x):>6.3f}")
# Envy: value[i][b] = person i's value for bundle b; person i receives alloc[i].
value = [[10, 8, 3], [4, 9, 2], [6, 5, 7]]
alloc = [1, 0, 2] # person0->bundle1, person1->bundle0, person2->bundle2
print("\nenvy (best bundle I could see minus the one I got):")
envies = []
for i in range(3):
own = value[i][alloc[i]]
best = max(value[i][alloc[j]] for j in range(3))
envies.append(best - own)
print(f" person {i}: own={own} best_available={best} envy={best - own}")
print(f" max-envy = {max(envies)} (envy-free would be 0)")
assert round(jain([0.6] * 4), 3) == 1.0
assert jain([0.9, 0, 0, 0]) == 0.25 # equals 1/n, the worst case
assert max(envies) == 5
print("\nasserts passed")
# Output:
# case min mean var Jain Gini
# equal [.6,.6,.6,.6] 0.60 0.600 0.000 1.000 0.000
# skewed [.9,.9,.9,.1] 0.10 0.700 0.120 0.803 0.214
# one-hogs [.9, 0, 0, 0] 0.00 0.225 0.152 0.250 0.750
#
# envy (best bundle I could see minus the one I got):
# person 0: own=8 best_available=10 envy=2
# person 1: own=4 best_available=9 envy=5
# person 2: own=7 best_available=7 envy=0
# max-envy = 5 (envy-free would be 0)
#
# asserts passed
6. Real-world example
A cloud team promised each tenant "fair" access to a shared pool of GPU minutes and reported a single average-utilization number that looked healthy at 72%. A tenant escalated anyway: their jobs kept starving. When the team finally computed a distribution, the picture flipped — utilization was [0.98, 0.95, 0.93, 0.06] across four tenants. The mean (0.73) matched the dashboard, but the floor was 0.06 and Jain was 0.80, both screaming that one tenant was being starved while the aggregate looked fine. The fix was not a bigger pool; it was a scheduler change plus a floor guarantee (no tenant below a set minimum) with Jain tracked over time as the health metric. The general lesson is the same one that recurs across fairness work: an average can be healthy while a minority is being crushed, and only a floor-or-spread metric reveals it. Jain's index was literally invented for this — measuring fairness of resource allocation in shared computer systems.
7. Interview questions companies actually ask
Q1. Why report a fairness metric instead of just the average? Because the average is blind to distribution. Two systems with identical mean utilization can differ wildly — one equal, one starving a minority — and only a floor (min) or spread (Jain/Gini) metric distinguishes them. The average is a necessary but never sufficient fairness signal.
Q2. What does the Jain index measure, and what's its range? It measures how equally a quantity is shared: (Σxᵢ)² / (n·Σxᵢ²), ranging from 1/n (one entity has everything) to 1 (perfectly equal). It is scale-free and population-independent, so it compares cleanly across group sizes and magnitudes — which is why it is standard in networking.
Q3. Jain vs Gini — how do they differ and which way do they point? Both are spread measures, but they move in opposite directions: high Jain is good (equal), low Gini is good (equal). Gini is the normalized mean pairwise difference and reacts to inequality across the whole range; Jain is tied to squared shares and reacts strongly to a few dominant shares. Reporting both is common because they emphasize different tails.
Q4. What is envy-freeness and why isn't low variance enough? An allocation is envy-free if no one would prefer another's share. Low variance means the received amounts are similar; it says nothing about preferences. If people value shares differently, an equal-looking split can still be envious, and a slightly unequal split can be envy-free. Envy captures felt fairness that variance misses — at the cost of needing everyone's valuation of every share.
Q5. A split has Gini 0.75 — is that bad? On its own it signals high inequality (Gini 0 is equal), but "bad" depends on the domain and whether inequality reflects legitimate need or priority. Always pair it with the floor (is anyone starved?) and, if preferences differ, an envy check. A number without context invites the wrong fix.
Q6. When do these metrics mislead you? When shares aren't cardinal or comparable (ranks only), or when fairness should mean serving the greatest need first. A low-Gini split can still be unjust if it ignores that some participants needed more. In those cases use ordinal/voting notions or priority (max-min by need) instead of equality metrics.
Q7. How would you monitor fairness in production? Track a trio over time, not one number: a floor (min or a low percentile), a spread (Jain or Gini), and — where preferences vary — an envy or per-group satisfaction gap. Alert on the floor first; it is the one that maps to a starved user.
8. When to use / tradeoffs
Reach for these metrics when:
- You allocate a divisible resource or a shared satisfaction and need to quantify fairness, not just assert it.
- A stakeholder disputes an average ("it's fine on average") and you need to expose a starved minority.
- You are tuning a policy (a scheduler, a ranker, a
lambdafairness knob) and need an objective to compare settings.
Do NOT rely on them when:
| Situation | Why it breaks | Use instead |
|---|---|---|
| Only rankings are available, not amounts | Jain/Gini need cardinal shares | ordinal/voting fairness notions |
| Fairness should track need, not equality | equal split can starve the needy | max-min by need / priority rules |
| Preferences differ across people | equal amounts can still be envied | envy-freeness (per-pair check) |
| You report just one number | each metric hides a different failure | a floor + spread + envy trio |
| Values can be negative or zero-mean | Gini's /mean and Jain assume nonneg shares | shift/rescale, or use a suited measure |
Honest limits. Jain and Gini are summary numbers: two very different distributions can share a Gini value, so they compress away detail you may need. Both assume non-negative, comparable shares; on zero or negative means the formulas degrade (the code guards the mean == 0 case). Envy is the most faithful to felt fairness but the most expensive and data-hungry (n² valuations), so it is often skipped precisely where it matters most. And all of them score a single snapshot — none captures whether unfairness now is compensated later, which repeated-allocation systems must measure separately.
9. Summary + related articles
- Fairness is not one number: check a floor (min), a spread (Jain or Gini), and envy together.
- Jain runs
1/n → 1(higher = equal), is scale-free and size-independent; Gini runs0 → ~1(lower = equal) — they point opposite ways. - Min is a guardrail that ignores everyone above the floor; envy captures felt fairness that spread metrics miss but needs everyone's valuations.
- An average can be healthy while a minority is starved — the floor/spread metrics are what reveal it (Jain was invented for exactly this).
- Boundary: these assume cardinal, comparable, non-negative shares; for rank-only or need-based fairness they mislead — use voting or priority rules.
Related:
- Fair Aggregation: Balancing Utility and Fairness — how these metrics score the choices an aggregation rule makes.
- Weighted Averages & Aggregation — the mean machinery underneath variance, Jain, and Gini.
- Recommendation Systems — where fairness metrics are applied to ranked results for groups.
Resources
- Jain, R., Chiu, D.-M., Hawe, W. (1984). "A Quantitative Measure of Fairness and Discrimination for Resource Allocation in Shared Computer Systems." DEC Technical Report TR-301 — the source of the Jain index. (Report number confirmed; page range not verified here.)
- Gini, C. (1912). "Variabilità e mutabilità" — the original inequality measure; most readers should use a modern statistics text's treatment of the Gini coefficient.
- Foley, D. (1967). "Resource Allocation and the Public Sector" — an early formalization of envy-freeness. (Venue/pages not verified here.)
- Masthoff, J. (2015). "Group Recommender Systems: Aggregation, Satisfaction and Group Attributes." In Recommender Systems Handbook (2nd ed.), Springer — connects these fairness measures to group decision rules.