A puzzle addressed during a MathsJam session (January 2016).
Three equilateral triangles. Which fraction is shaded?
Found on Twitter, posted by @math8_teacher
Well, time for some geometry then. Hmm what about being lazy instead and let Python do the calculations? :)
from sympy.geometry import Point, Segment, Triangle, intersection
from sympy import sqrt
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
def ratio(side):
# Height of an equilateral triangle
height = side * sqrt(3) / 2
# Points of the triangles on the base line
p0 = Point(0, 0)
p1 = Point(side, 0)
p2 = Point(2 * side, 0)
p3 = Point(3 * side, 0)
# Points of the triangles on the "top" line
p4 = Point(0.5 * (p1.x + p0.x), height)
p5 = Point(0.5 * (p2.x + p1.x), height)
p6 = Point(0.5 * (p3.x + p2.x), height)
# The 3 segments we are only interested in
# (that will intersect the last one)
s1 = Segment(p1, p4)
s2 = Segment(p1, p5)
s3 = Segment(p2, p5)
# The "big" segment that intersects everything
s4 = Segment(p0, p6)
# Intersections of the "big" segment with the others
i1 = intersection(s1, s4)[0]
i2 = intersection(s2, s4)[0]
i3 = intersection(s3, s4)[0]
# The normal triangles
tri1 = Triangle(p0, p1, p4)
tri2 = Triangle(p1, p2, p5)
tri3 = Triangle(p2, p3, p6)
# The triangles constructed with the intersection points
tri4 = Triangle(p0, i1, p4)
tri5 = Triangle(i2, i3, p5)
res = (tri4.area + tri5.area) / (tri1.area + tri2.area + tri3.area)
return res
# Calculate for 3 triangles of side 1
print('Ratio:', ratio(1))
Ratio: 5/18
# The ratio should not depend on the side of the triangles
for i in range(2, 10):
assert ratio(i) == ratio(1)
print('All good!')
All good!
The figure only shows 3 triangles. And for now the code does the calculation in a very manual way, defining the points, segments, triangles one by one.
Is it possible to rewrite the ratio
function to be generic and handle the case for more triangles?
def ratio_n(n, side):
# Height of an equilateral triangle
height = side * sqrt(3) / 2
ps = []
# Points of the triangles on the base line
for k in range(n + 1):
ps.append(Point(side * k, 0))
# Points of the triangles on the "top" line
for k in range(n):
ps.append(Point(0.5 * (ps[k+1].x + ps[k].x), height))
# The segments we are only interested in
segs = []
for k in range(1, n):
segs.append(Segment(ps[k], ps[k+n]))
segs.append(Segment(ps[k], ps[k+n+1]))
# The "big" segment that intersects everything
big = Segment(ps[0], ps[-1])
# The normal triangles
tris = []
for k in range(n):
tris.append(Triangle(ps[k], ps[k+1], ps[k+1+n]))
# The triangles constructed with the intersection points
shaded = []
for k in range(n-1):
if k == 0:
it = intersection(segs[0], big)[0]
shaded.append(Triangle(ps[k], it, ps[k+1+n]))
else:
kk = 2 * k
it1 = intersection(segs[kk], big)[0]
it2 = intersection(segs[kk-1], big)[0]
shaded.append(Triangle(it1, it2, ps[k+1+n]))
area_shaded = sum([abs(tri.area) for tri in shaded])
area_total = sum([abs(tri.area) for tri in tris])
res = area_shaded / area_total
return res
Let's first verify the function calcultes the same result found above for 3 triangles.
print('Ratio for 3 triangles with the generic function:', ratio_n(3, 1))
Ratio for 3 triangles with the generic function: 5/18
Good. Now what happens when there are more triangles?
SIDE = 10
LIMIT = 31 # Up to 30 triangles
ratios = []
xs = list(range(3, LIMIT))
for i in xs:
ratio_i = ratio_n(i, SIDE)
ratios.append(ratio_i)
print('Ratio for', i, 'triangles:', ratio_i, '~=', ratio_i.evalf())
Ratio for 3 triangles: 5/18 ~= 0.277777777777778 Ratio for 4 triangles: 7/24 ~= 0.291666666666667 Ratio for 5 triangles: 3/10 ~= 0.300000000000000 Ratio for 6 triangles: 11/36 ~= 0.305555555555556 Ratio for 7 triangles: 13/42 ~= 0.309523809523810 Ratio for 8 triangles: 5/16 ~= 0.312500000000000 Ratio for 9 triangles: 17/54 ~= 0.314814814814815 Ratio for 10 triangles: 19/60 ~= 0.316666666666667 Ratio for 11 triangles: 7/22 ~= 0.318181818181818 Ratio for 12 triangles: 23/72 ~= 0.319444444444444 Ratio for 13 triangles: 25/78 ~= 0.320512820512820 Ratio for 14 triangles: 9/28 ~= 0.321428571428571 Ratio for 15 triangles: 29/90 ~= 0.322222222222222 Ratio for 16 triangles: 31/96 ~= 0.322916666666667 Ratio for 17 triangles: 11/34 ~= 0.323529411764706 Ratio for 18 triangles: 35/108 ~= 0.324074074074074 Ratio for 19 triangles: 37/114 ~= 0.324561403508772 Ratio for 20 triangles: 13/40 ~= 0.325000000000000 Ratio for 21 triangles: 41/126 ~= 0.325396825396825 Ratio for 22 triangles: 43/132 ~= 0.325757575757576 Ratio for 23 triangles: 15/46 ~= 0.326086956521739 Ratio for 24 triangles: 47/144 ~= 0.326388888888889 Ratio for 25 triangles: 49/150 ~= 0.326666666666667 Ratio for 26 triangles: 17/52 ~= 0.326923076923077 Ratio for 27 triangles: 53/162 ~= 0.327160493827161 Ratio for 28 triangles: 55/168 ~= 0.327380952380952 Ratio for 29 triangles: 19/58 ~= 0.327586206896552 Ratio for 30 triangles: 59/180 ~= 0.327777777777778
plt.figure(figsize=(20,20), dpi=80)
plt.plot(xs, ratios, 'o-')
plt.xlabel('Number of equilateral triangles')
plt.ylabel('Percentage of the shaded area')
plt.title('Percentage of shaded area per number of equilateral triangles')
<matplotlib.text.Text at 0x7f577bb172e8>
That's quite interesting! With some optimizations in the ratio_n
function, we could calculate the ratio for more triangles.
But looking at the graph, we can ask at least two questions:
I will let these two questions as an open challenge :)