Books · The Fiddler: Solutions
Chapter 67
Can You Squeeze the Heart?
Build a heart by attaching a semicircle of radius to each of two adjacent edges of a unit square, both bulging outward. What is the radius of the smallest circle that contains the heart?
The Fiddler, Zach Wissner-Gross, February 14, 2025(original post)
Solution
Place the square as with lobes on the top and right edges; the figure is symmetric about the diagonal , so the enclosing circle is centred at some . Two kinds of point fight to be farthest: the tip at the opposite corner , at distance , and a lobe apex, at distance (centre-to-lobe plus the lobe radius). The optimum balances them: Squaring and simplifying collapses the quadratic terms and leaves , so and
The computation
Build the heart’s boundary as points, square edges plus the two semicircular arcs, then find the centre that minimises the farthest distance (the smallest enclosing circle). Its radius matches .
import numpy as np
from scipy.optimize import minimize
P = [(t, 0) for t in np.linspace(0, 1, 200)] + [(0, t) for t in np.linspace(0, 1, 200)]
P += [(0.5 + 0.5*np.cos(a), 1 + 0.5*np.sin(a)) for a in np.linspace(0, np.pi, 200)]
P += [(1 + 0.5*np.cos(a), 0.5 + 0.5*np.sin(a)) for a in np.linspace(-np.pi/2, np.pi/2, 200)]
P = np.array(P)
f = lambda c: np.max(np.hypot(P[:, 0] - c[0], P[:, 1] - c[1]))
print(minimize(f, [0.6, 0.6], method='Nelder-Mead').fun, (3*np.sqrt(2)+2)/7) # 0.89181
Extra Credit
What is the radius of the smallest circle containing two non-overlapping hearts?
Solution
This is a genuinely hard non-convex packing, and the official value is paywalled. Two disjoint hearts side by side give the rigorous upper bound . Letting them interlock does better: rotating one heart and nesting it against the other, a numerical search settles on an enclosing circle of radius about a valid arrangement found by search, not a claimed optimum.
The computation
Model each heart as an interior point cloud (for the no-overlap test) and a boundary point cloud (for the enclosing circle). From many random placements of the second heart, reject any that overlaps the first and keep the smallest enclosing circle of the pair.
def in_heart(P): # interior test, heart at identity
x, y = P[..., 0], P[..., 1]
return (((x >= 0) & (x <= 1) & (y >= 0) & (y <= 1))
| ((y >= 1) & ((x - .5)**2 + (y - 1)**2 <= .25))
| ((x >= 1) & ((x - 1)**2 + (y - .5)**2 <= .25)))
t = np.linspace(0, 1, 60); a = np.linspace(0, np.pi, 90)
BND = np.vstack([np.c_[t*0, t], np.c_[t, t*0],
np.c_[.5 + .5*np.cos(a), 1 + .5*np.sin(a)],
np.c_[1 + .5*np.cos(a - np.pi/2), .5 + .5*np.sin(a - np.pi/2)]])
gx, gy = np.meshgrid(np.linspace(-.02, 1.52, 120), np.linspace(-.02, 1.52, 120))
FILL = np.c_[gx.ravel(), gy.ravel()]; FILL = FILL[in_heart(FILL)]; cen = np.array([.5, .5])
def place(pr, Q):
phi, tx, ty = pr; c, s = np.cos(phi), np.sin(phi)
return (Q - cen) @ np.array([[c, s], [-s, c]]) + cen + np.array([tx, ty])
enc = lambda pts: minimize(lambda c: np.max(np.hypot(*(pts - c).T)),
pts.mean(0), method='Nelder-Mead').fun
rng = np.random.default_rng(0); best = 9e9
for _ in range(150):
def obj(pr):
if max(abs(pr[1]), abs(pr[2])) > 3 or in_heart(place(pr, FILL)).any(): return 50.0
return enc(np.vstack([BND, place(pr, BND)]))
r = minimize(obj, [rng.uniform(0, 2*np.pi), *rng.uniform(-1.6, 1.6, 2)],
method='Nelder-Mead', options={'maxiter': 250, 'xatol': 1e-3, 'fatol': 1e-3})
if obj(r.x) < 49: best = min(best, r.fun)
print(round(best, 3)) # 1.497