%matplotlib inline
import math
import matplotlib.pyplot as plt
Imagine a red circle.
Around the red circle, imagine a set of identical yellow circles; each yellow circle is tangent to the red circle and to each of its neighbors, like balls in a ring bearing.
Around the yellow circles, imagine a surrounding circle tangent to each of the yellow circles (so this circle and the red circle are concentric). Any area inside the outer circle that's not already red or yellow, color it blue.
Let the radius of each yellow circle be $r$, and the radius of the red circle be $R$.
What ratio of $r$ to $R$ maximizes the blue area? The number of yellow circles can vary as needed, as long as the other qualities are met (no gaps between yellow circles, no partial circles).
Because the problem statement only cares about the ratio of $r$ to $R$, I'll fix $R = 1$ here. This makes $r$ (the radius of the yellow circles) the same as the ratio.
The relationship between $r$ and $n$ is fixed geometrically:
ax = plt.gca()
ax.cla()
ax.set_xlim((0, 2))
ax.set_ylim((0, 2))
ax.set_aspect("equal")
theta = 0.34 # guesstimate for illustration purposes
# Plot red circle
red_radius = 1.0
ax.add_artist(plt.Circle((0, 0), red_radius, color='red', fill=False))
# Plot two yellow circles
yellow_radius = 0.5
c1_x = (red_radius + yellow_radius) * math.cos(theta * 0.0)
c1_y = (red_radius + yellow_radius) * math.sin(theta * 0.0)
c2_x = (red_radius + yellow_radius) * math.cos(theta * 2.0)
c2_y = (red_radius + yellow_radius) * math.sin(theta * 2.0)
c12_x = (c1_x + c2_x) / 2.0
c12_y = (c1_y + c2_y) / 2.0
ax.add_artist(plt.Circle((c1_x, c1_y), yellow_radius, color='orange', fill=False))
ax.add_artist(plt.Circle((c2_x, c2_y), yellow_radius, color='orange', fill=False))
plt.plot(
[0, c1_x, c2_x, 0, c12_x],
[0, c1_y, c2_y, 0, c12_y],
)
# Plot blue circle
ax.add_artist(plt.Circle((0, 0), red_radius + (yellow_radius * 2), color="blue", fill=False))
# Add labels
ax.annotate("\N{GREEK CAPITAL LETTER THETA}", (0.3, 0.03))
ax.annotate("1 + r", (0.35, 0.5))
ax.annotate("r", (1.3, 0.75))
ax.annotate("r", (1.45, 0.2))
Text(1.45, 0.2, 'r')
$n$, as defined above, is an integer that relates to $\theta$ as follows:
$$ \theta = \frac{\pi}{n} $$This shows that $r$ and $n$ are related, which makes sense intuitively. We know that the yellow circles can't be any arbitrary size for a given number of circles, which means that one implies the other. Let's solve this relation:
\begin{align} \tan{\frac{\pi}{n}} &= \frac{r}{1 + r} \\ \frac{\pi}{n} &= \arctan{\frac{r}{1 + r}} \\ \frac{1}{n} &= \frac{\arctan{\frac{r}{1 + r}}}{\pi} \\ n &= \frac{\pi}{\arctan{\frac{r}{1 + r}}} \\ \end{align}Going the other way, to solve for $r$:
\begin{align} \frac{r}{1 + r} &= \tan{\frac{\pi}{n}} \\ r &= \tan{\frac{\pi}{n}} (1 + r) \\ r &= \tan{\frac{\pi}{n}} + r \tan{\frac{\pi}{n}} \\ r(1 - \tan{\frac{\pi}{n}}) &= \tan{\frac{\pi}{n}} \\ r &= \frac{\tan{\frac{\pi}{n}}}{1 - \tan{\frac{\pi}{n}}} \mathrm{where} \tan{\frac{\pi}{n}} \ne 1 \\ \end{align}Now that we have those two equations, let's plot some integer solutions for $n$:
def r(n):
tan_pi_n = math.tan(math.pi / n)
assert not math.isnan(tan_pi_n)
r = tan_pi_n / (1.0 - tan_pi_n)
#print(f"For n = {n:>2}, tan(pi/n) = {tan_pi_n:.5f}, r = {r:.5f}")
return r
xs = list(range(3, 20))
ax = plt.gca()
ax.set_xlim((min(xs) - 1, max(xs)))
ax.set_ylim((0, 20))
plt.plot(xs, [r(n) for n in xs], color='green', label='r')
[<matplotlib.lines.Line2D at 0x109c49c90>]
This sanity check worked: the more circles we have, the smaller they need to be to not overlap.
Now let's look at the areas and see if there's a pattern there.
def yellow_area(n):
return n * math.pi * r(n) ** 2
def blue_area(n):
yellow_radius = r(n)
blue_radius = 1 + (2.0 * yellow_radius)
orig_area = math.pi * (blue_radius ** 2)
#print(f"orig blue area with n = {n:>2}: {orig_area:.3f} (blue radius: {blue_radius:.5f}))")
red_area = math.pi
return orig_area - (red_area + yellow_area(n))
ax = plt.gca()
ax.set_xlim((min(xs) - 1, max(xs)))
ax.set_ylim((0, 20))
plt.plot(xs, [yellow_area(n) for n in xs], color='orange', label='yellow area')
plt.plot(xs, [blue_area(n) for n in xs], color='blue', label='blue area')
print(math.sqrt(3) / (1 - math.sqrt(3)))
-2.366025403784439
There appears to be something something strange happening in the cases where $n \in \{3, 4\}$. At $n \ge 5$, it appears that the blue area shrinks down further and further (which is what we're trying to maximize). Let's look at these two cases analytically and see what's going on.
\begin{align} r(3) &= \frac{\tan{\frac{\pi}{3}}}{1 - \tan{\frac{\pi}{3}}} \\ r(3) &= \frac{\sqrt{3}}{1 - \sqrt{3}} \\ r(3) &\approx -2.36603\ldots \\ \end{align}Plugging that into our area formulas gives us the following:
\begin{align} area_{blue}(3) &= \pi (1 + 2 r(3))^2 - \pi - 3 \pi r(3)^2 \\ area_{blue}(3) &\approx \pi (1 + 2 (-2.36603))^2 - \pi - 3 \pi -2.36603^2 \\ area_{blue}(3) &\approx -12.1455 \\ \end{align}That doesn't make any sense! And that's definitely not giving us a maximum value for the blue area regardless, so we'll ignore 3 and look at 4:
\begin{align} r(4) &= \frac{\tan{\frac{\pi}{4}}}{1 - \tan{\frac{\pi}{4}}} \\ r(4) &= \frac{1}{1 - 1} \\ r(4) &= \frac{1}{0} \\ \end{align}The division by zero means that our path ends there. Updating our assumptions to note that $n \ge 5$, we can work on finding the maximum. Based on the plot earlier, it certainly looks like the minimum is going to exist at 5. While we could solve the derivative and do a bunch of calculus, $\frac{d}{dn} area_{blue}(n)$ is a monster of an equation, and I don't think we need to be that rigorous here.
Based on the intution from the plot above and the answers we solved where the plot is discontinuous, I think it's safe to say that you want 5 yellow circles to maximize the blue area. This implies the following derivation for $r$:
\begin{align} r &= \frac{\tan{\frac{\pi}{n}}}{1 - \tan{\frac{\pi}{n}}} \\ r &= \frac{\tan{\frac{\pi}{5}}}{1 - \tan{\frac{\pi}{5}}} \\ \end{align}…which evaluates to:
print("Solution: r = {:.5f}".format(r(5)))
Solution: r = 2.65688