#!/usr/bin/env python
# coding: utf-8
# # Triangle Fraction
#
# A puzzle addressed during a MathsJam session (January 2016).
#
# ![Triangle Fraction Problem](https://gist.githubusercontent.com/jtpio/3f222251a25aa0b7898e/raw/8afe6d2a0d5d0fdc8665a621786db1647997490c/triangles.png)
#
# > Three equilateral triangles. Which fraction is shaded?
#
# Found on Twitter, posted by [@math8_teacher](https://twitter.com/math8_teacher/status/687639479457153025)
#
# Well, time for some geometry then. Hmm what about being lazy instead and let Python do the calculations? :)
# In[1]:
from sympy.geometry import Point, Segment, Triangle, intersection
from sympy import sqrt
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
import seaborn as sns
# In[2]:
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
# In[9]:
# Calculate for 3 triangles of side 1
print('Ratio:', ratio(1))
# In[4]:
# 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!')
# ## Extension for 4, 5, and more triangles
#
# 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?
# In[5]:
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.
# In[6]:
print('Ratio for 3 triangles with the generic function:', ratio_n(3, 1))
# Good. Now what happens when there are more triangles?
# In[7]:
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())
# In[8]:
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')
# ## Observations
#
# 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:
#
# - Does the ratio converge to 1/3? (0.333333...)
# - If yes, how to prove it?
#
# I will let these two questions as an open challenge :)