#!/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 :)