We show that associating metallic means with polygons, beyond the golden and silver means, requires looking beyond the side to diagonal relationship.

Introduction

From Polygons, Diagonals, and the Bronze Mean by Antonia R. Buitrago

The Metallic Means Family (MMF) was introduced in 1998 by Vera W. de Spinadel [1998; 1999]. Its members are the positive solutions of the quadratic equation x2pxq=0, where the parameters p and q are positive integer numbers. The more relevant of them are the Golden Mean and the Silver Mean, the first members of the subfamily which is obtained by considering q=1. The members σp, p=1,2,3 of this subfamily share properties which are the generalization of the Golden Mean properties. For instance, they all may be obtained by the limit of consecutive terms of certain "generalized secondary Fibonacci sequences" (GSFS) and they are the only numbers which yield geometric sequences:

,1σp3,1σp2,1σp,1,σp,σp2,σp3,

with additive properties

1+pσp=σp2
σpk+pσpk+1=σpk+2
1σpk=pσpk+1+1σpk+2, k=1,2,3, .

Figure 1

In Polygons, Diagonals, and the Bronze Mean, Antonia Redondo Buitrago demonstrates that the bronze mean does not appear as a side to diagonal relationship in a regular polygon. Yet there still remains the question of whether or not metallic means appear as a ratio between line segments like in the pentagon, Figure 1.

Logic

n=7
Figure 2
n=8
Figure 3

Let there be a regular polygon with n sides and vertices and a circumradius of 1. Connect each vertex pair with a diagonal di and let Pdi be a set of intersection points between di and each other diagonal:

Pdi={p1,p2,p3,,pj}

where j is the total number of intersection points for a given diagonal di.

Let Ldi be the set of lengths between pairs of points in Pdi:

Ldi={p1p2¯,p1p3¯,,pj1pj¯}.

For a regular polygon, only a single vertex and its diagonals are needed due to rotational symmetry.

For a vertex, the number of diagonals required to encompass all possible lengths of a regular polygon is n2 due to reflectional symmetry. Reflectional symmetry reduces the number of diagonals by half because diagonals opposite each other across a line of symmetry have the same set of line segments; even-sided polygons also include the diagonal that is the circumdiameter.

Taking figure 2 as an example,

Ld1=Ld6, Ld2=Ld5, Ld3=Ld4.

And for figure 3,

Ld1=Ld7, Ld2=Ld6, Ld3=Ld5, Ld4=Ld4.

Then, the complete set of lengths for a regular polygon with n-sides is

Ln=i=1n2Ldi.

Then to test if a metallic mean σp exists as a ratio of lengths in Ln, we find the intersection between Ln and σpLn:

Mσp=Ln(σpLn) ={xLn:lLn(x=σpl)}. 

If the intersection Mσp is not empty then the metallic mean exists as a ratio of lengths.

Example

Let's take a look at a simple example, the pentagon, which has an established relationship with the first metallic mean, the golden mean φ. The pentagon has the characteristic that any diagonal is representative of all diagonals so we'll use the blue diagonal from figure 1.

Let L5 be the complete set of line segments for a pentagon

L5={AB¯,AC¯,AD¯,BC¯,BD¯,CD¯}

and the lengths are

L5={0.44902,0.72654,1.17557,1.90211}.

So to test for the first metallic mean, we multiply L5 by σ1, where σ1=1.61803,

σ1L5={0.72654,1.17557,1.90211,3.07768}

and we find that all but one is a member of L5:

Mσ1={0.72654,1.17557,1.90211}.

Additionally we can test for the fourth metallic mean by multiplying L5 by σ4 (φ3), where σ4=4.23606,

σ4L5={1.90211,3.07768,4.97979,8.05748}

and we find that only one is a member of L5:

Mσ4={1.90211}.

Program

Download zip | GitHub repo
install required packages: pip3 install -r requirements.txt

usage: main.py [-h] [--dps DPS] [--buffer BUFFER] [-s] n mean_id

positional arguments:
  n                   # of sides for regular polygon
  mean_id             metallic mean to test for

optional arguments:
  -h, --help          show this help message and exit
  --dps DPS           decimal precision [default: 50]
  --buffer BUFFER     decimal precision buffer [default: 5]
  -s, --show_matches  show matches

examples: 
python3 main.py 5 1
python3 main.py 8 2
python3 main.py 13 3

The program is written in Python3.7 using mpmath for arbitrary-precision. mpmath is required because double-precision has an insufficient number of significant digits to account for the very tiny variations in lengths which result in false positives.

mpmath is set to 55 decimal-places, an arbitrarily sufficient number, with 5 decimal-places as a buffer to account for minor cumulative computational errors. Then the computed mpmath values are converted to decimal string literals with 50 significant digits allowing for string comparisons of relatively precise decimal values.

lib/utils.py
import mpmath as mp

# nstr() is a mpmath function that converts an mpf or mpc
# to a decimal string literal with n significant digits.
def getkey(val): return mp.nstr(val, SIG_DIGITS)

# return metallic mean for specified n
def mmf(n): return (n+mp.sqrt(n*n+4))/2
lib/vector2.py
import mpmath as mp
from .utils import getkey

class Vector2(object):
    def __init__(self, x=0, y=0):
        if mp.almosteq(x, 0): x = 0.0
        if mp.almosteq(y, 0): y = 0.0

        self.x = mp.mpf(x)
        self.y = mp.mpf(y)

    def __add__(self, other):
        return Vector2(self.x+other.x, self.y+other.y)

    def __sub__(self, other):
        return Vector2(self.x-other.x, self.y-other.y)

    def __mul__(self, other):
        if isinstance(other, Vector2):
            return Vector2(self.x*other.x, self.y*other.y)
        else:
            return Vector2(self.x*other, self.y*other)

    def __str__(self):
        return '<Vector2 x=%s y=%s>'%(str(self.x)[:10], str(self.y)[:10])

    def __repr__(self):
        return self.__str__()

    def __hash__(self):
        return hash((getkey(self.x), getkey(self.y)))

    def __eq__(self, other):
        return mp.almosteq(self.x, other.x) and mp.almosteq(self.y, other.y)

    def cross(self, other):
        return self.x*other.y - self.y*other.x

    def dist(self, other):
        return (self-other).len()

    def dot(self, other):
        return self.x*other.x + self.y*other.y

    def len(self):
        return mp.sqrt(self.dot(self))
lib/linesegment.py
import mpmath as mp

class LineSegment(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.vec = end-start

    def intersect(self, other):
        numerator = (other.start-self.start).cross(other.vec)
        denominator = self.vec.cross(other.vec)

        if (not numerator or mp.isnormal(numerator)) and mp.isnormal(denominator):
            ratio = numerator/denominator
            return self.vec*ratio + self.start

        return None

1. Generate the vertices of the polygons and connect each pair of vertices with a diagonal.

main.py
# n is polygon size
theta = 2*pi/n

# calculate vertices
vertices = []
for i in range(n):
    x = cos(theta*i)
    y = sin(theta*i)
    vertex = Vector2(x, y)
    vertices.append(vertex)

# generate diagonals
diagonals = []
for i in range(n):
    for j in range(i+1, n):
        v1 = vertices[i]
        v2 = vertices[j]
        diag = LineSegment(v1, v2)
        diagonals.append(diag)

2. For each diagonal calculate and collect intersection points between itself and other diagonals. Then within the same iteration calculate and collect distances (lengths) between pairs of points in a dictionary using a consistent key for the mpmath value.

main.py
# calculate line segments lengths
lengths = dict()
for i, diag in enumerate(diagonals[:n//2]):
    # find intersection points
    points = set()
    for other in diagonals[i+1:]:
        # find the intersection point between diagonals
        point = diag.intersect(other)

        # ensure point lies within unit circle
        if point and point not in points and abs(point) <= 1:
            points.add(point)

    # calculate lengths for pairs of points on the same diagonal
    if len(points):
        points = list(points)
        for j, point1 in enumerate(points):
            for point2 in points[j+1:]:
                dist = point2.dist(point1)
                key = getkey(dist)
                if key not in lengths:
                    lengths[key] = dist

3. Iterate over the set and multiply each length by a metallic mean and test to see if the resultant value is contained in the length set.

main.py
# test for specified metallic mean
count = 0
for k,l in lengths.items():
    key = getkey(mean*l)
    if key in lengths:
        count += 1

Metallic mean selection

We could test every polygon with every metallic mean but eventually the number of lengths grows too large to compute by hand or a modern desktop computer. So it is prudent to find a method for determining appropriate metallic means for polygons or conversely, a method for identifying prospective polygons for metallic means.

Now back to the definition of a metallic mean. They are the positive solutions of the characteristic equation x2pxq=0 when q:=1 and p is a positive integer. Then the quadratic equation is

x2px1=0

and the quadratic formula to compute the means is

σp=p+p2+42.

Selecting appropriate polygons to test for metallic means σp appears to be related either to the discriminant, p2+4, or the simplified form of the radical p2+4. metallic means that are a power of a lower mean appear alongside their lower counterpart.

In the case of p=1, the golden mean φ, the discriminant is 5 and the simplified radical is 5, associating the metallic mean with the pentagon and polygons with a number of sides that is a multiple of 5. For p=4, φ3, the discriminant is 20 and the simplified radical is 25 which is also associated with the pentagon and polygons with a number of sides that is a multiple of 5. For p=11, φ5, the discriminant is 125 and the simplified radical is 55 but does not appear until n is a multiple of 10 or 15. Similarily p=29 appears when n is a multiple of 20 or 30 and p=76 appears when n is a multiple of 30.

In the case of p=3, the discriminant is 13 and the radical is 13, associating p=3 to the tridecagon.

In the case of p=6, the discriminant is 40 and the simplified radical is 210. The associated polygon is a 40-gon.

In the case of p=8, the discriminant is 68 but the associated regular polygon is a heptadecagon (n=17), which relates to the simplified radical 217.

* As of writing this, I have been unable to determine values for n when p is 7,9 or 10 for p10.

Visualization tool

Metallic Means results review tool thumbnail
Tool to visualize the Metallic Mean matches.

Left & right arrow keys – pagination
Up & down arrow keys – list selection
Spacebar – Play/Pause

Results

p Radical n-gons Trigonometric expression
1
5
5x
2cosπ5
2
8
8x
tan3π8
3
13
13x Simplified (Wikipedia)
8cosπ13cos3π13cos4π13

Original
2cosπ13(sin2π13csc3π13+1)
4
25
5x
8cos3π5
5
29
29x Simplified (Wikipedia)
128cosπ29cos4π29cos5π29cos6π29cos7π29cos9π29cos13π29

Original
sin19π2912csc19π29(cos25π29+1)sin25π29sin3π29csc20π29sin25π29sin5π29csc21π29sin16π29sinπ29csc7π29
6
40
40x
sin21π40sin7π8sin5π8sinπ40csc11π40sin19π20sin9π40csc7π10
7
53
53, 106
8
217
17x
sin6π17sin7π17sin3π17sin12π17sinπ17sin9π17sin4π17sin2π17
9
85
85
10
104
52, 104
11
55
10+5x
32cos5π5
12
237
74
sin8π37sin5π74sin13π74sin17π74sin31π74sin11π37sinπ74sin7π74sin9π74sin33π74

If anyone happens to identify 7, 9 or 10 please let me know at tellerm<at>protonmail.com or on Twitter @tellerm. Thank you.

References

[1] Vera W. de Spinadel. (1999). The Family of metallic means. Visual Mathematics 1, 3. http://members.tripod.com/vismath1/spinadel/

[2] Antonia R. Buitrago. (2007). "Polygons, Diagonals and the Bronze Mean", Nexus Network Journal 9: 321.