Chapter 104
The Perplexing Puzzle Of The Proud Partygoers
A group of people are at a party, some pairs of them friends (friendship is mutual). Everyone has at least one friend present. Call a guest proud if her own number of friends is strictly larger than the average number of friends her friends have. More than one guest can be proud. How large can the share of proud guests be?
The Riddler, FiveThirtyEight (Dominic van der Zypen)(original post)
Solution
Pride is a contrast: you are proud when you are better-connected than the friends you keep. The way to manufacture it wholesale is to make almost everyone the better-connected one, and a single missing friendship does exactly that.
Start from the fully connected party, where everyone is friends with everyone. All guests have friends, so nobody’s count beats the average around them, and no one is proud. Now remove one friendship, between two guests and . Their counts fall to ; every other guest still has .
Look at any of the other guests. She has friends, and that circle includes both and (each at ) alongside guests at . Her friends’ average is therefore a shade below , and her own strictly tops it. She is proud. Now look at . His friends are everyone except , and none of them lost an edge, so each still has friends. Their average is , above his ; he is not proud, and by symmetry neither is .
So all but two guests are proud, a share of which climbs toward as the party grows. The two stragglers are unavoidable: in any party the most-connected guest has every friend at a friend-count no larger than her own, so her friends’ average can never fall strictly below her count, and she is never proud. That keeps the share under always, and an exhaustive check of small parties confirms proud guests is the most any arrangement allows.
The computation
Build the complete graph on guests, delete one edge, and count the proud. A separate brute force over every graph on a handful of guests confirms that is genuinely the ceiling, not merely what this construction reaches.
from itertools import combinations, product
def proud_count(adj, N):
deg = {i: len(adj[i]) for i in range(N)}
return sum(1 for i in range(N)
if deg[i] > sum(deg[j] for j in adj[i]) / deg[i])
def complete_minus_edge(N): # the construction
adj = {i: set(range(N)) - {i} for i in range(N)}
adj[0].discard(1); adj[1].discard(0)
return proud_count(adj, N)
def best_over_all_graphs(N): # exhaustive optimum
edges, best = list(combinations(range(N), 2)), 0
for mask in product((0, 1), repeat=len(edges)):
adj = {i: set() for i in range(N)}
for on, (u, v) in zip(mask, edges):
if on: adj[u].add(v); adj[v].add(u)
if all(adj[i] for i in range(N)): # everyone needs a friend
best = max(best, proud_count(adj, N))
return best
for N in (4, 5, 6):
print(N, complete_minus_edge(N), best_over_all_graphs(N), N - 2)
# 4 2 2 2
# 5 3 3 3
# 6 4 4 4 <- construction meets the exhaustive maximum, both equal N-2