A curiosity journal of math, physics, programming, astronomy, and more.

Scaling stars on the flag

Anytime there's discussion of adding states to the United States, splitting states, or combining states, I usually start wondering about the U.S. flag and a new arrangements of stars. Currently the flag has 50 stars, one for each state, with an arrangement that looks like this:

50 stars

If states combine and the flag needs fewer than 50 stars, an arrangement from a historical flag could be adopted, unless the needed number of stars is 47, 42, 41, 40, 39, 22, 19, 18, 17, 16, 14, or anything below 13, counts for which there have never been official flags. If more states are added, Wikipedia lists possible designs for up to 55 states.

I'm curious to find an algorithm that generates suitable designs for any number of stars. Here's a breakdown of the past and possible structures for 48–55 stars:

StarsOverlapping grids
488×6
497×4, 7×3
506×5, 5×4
519×3, 8×3
527×4, 6×4
538×4, 7×3
549×6
559×3, 8×4 − 4 corners

The general design priorities seem fairly evident:

  1. Don't use grids that are too wide or too square.
  2. If the number of stars can be factored into two whole numbers, use one grid.
  3. If the number of stars can be decomposed into two grids whose number of rows differ from each other by no more than one and whose number of columns differ from each other by no more than one, then use two overlapping grids.
  4. If the number of stars can be decomposed into two grids whose number of rows differ by one, whose number of columns differ by one, and whose total number of stars is four more than the desired number of stars, then omit the corner stars from the taller grid.

Here are functions to calculate arrangements under rules #2, #3, and #4, each of which implements rule #1:

(defn one-grid-arrangement [stars]
  (loop [m (int (Math/sqrt stars))]
    (when-not (zero? m)
      (let [n (/ stars m)]
        (if (and (= n (int n))
                 (< 1 (/ n m) 2))
          [[n m]]
          (recur (dec m)))))))
(defn two-grid-arrangement [stars]
  (first
    (for [n1 (range 2 (* 2 (int (Math/sqrt stars))))
          m1 (range 2 n1)
          n2 [n1 (dec n1)]
          m2 [m1 (dec m1)]
          :when (and (= stars (+ (* n1 m1) (* n2 m2)))
                     (< 1 (/ n1 m1) 5)
                     (< 1 (/ n2 m2) 5))]
      [[n1 m1] [n2 m2]])))
(defn omitted-corners-arrangement [stars]
  (first
    (for [n1 (range 2 (* 2 (int (Math/sqrt stars))))
          m1 (range 2 n1)
          :let [n2 (dec n1)
                m2 (inc m1)]
          :when (and (= stars (- (+ (* n1 m1) (* n2 m2)) 4))
                     (< 1.5 (/ n1 m1) 5)
                     (< 1.5 (/ n2 m2) 5))]
      [[n1 m1] [n2 m2 :omit-corners]])))

We'll pick the first arrangement that works:

(def arrangement (some-fn one-grid-arrangement
                          two-grid-arrangement
                          omitted-corners-arrangement))

Let's confirm the arrangements are what we expect:

(map (juxt identity arrangement) (range 48 56))
([48 [[8 6]]]
 [49 [[7 4] [7 3]]]
 [50 [[6 5] [5 4]]]
 [51 [[9 3] [8 3]]]
 [52 [[7 4] [6 4]]]
 [53 [[8 4] [7 3]]]
 [54 [[9 6]]]
 [55 [[9 3] [8 4 :omit-corners]]])
48 stars
49 stars
50 stars
51 stars
52 stars
53 stars
54 stars
55 stars

With the calculations working, let's see how the stars on the flag might look as we scale up to 100 stars:

56 stars
57 stars
59 stars
60 stars
62 stars
63 stars
64 stars
65 stars
66 stars
67 stars
68 stars
69 stars
70 stars
72 stars
74 stars
75 stars
76 stars
77 stars
78 stars
80 stars
81 stars
83 stars
84 stars
85 stars
86 stars
88 stars
90 stars
91 stars
92 stars
94 stars
95 stars
96 stars
98 stars
99 stars
100 stars

These three simple design rules scale surprisingly far, though they don't allow for all possible counts between 50 and 100. They're unable to find acceptable patterns for 58, 61, 71, 73, 79, 82, 87, 89, 93, or 97 stars. We can, however, squeeze out designs for 58 and 79 stars if we omit corner stars from not just the taller grid but from both grids:

(defn omitted-corners-arrangement-2 [stars]
  (first
    (for [n1 (range 2 (* 2 (int (Math/sqrt stars))))
          m1 (range 2 n1)
          :let [n2 (dec n1)
                m2 (inc m1)]
          :when (and (= stars (- (+ (* n1 m1) (* n2 m2)) 8))
                     (< 1.5 (/ n1 m1) 5)
                     (< 1.5 (/ n2 m2) 5))]
      [[n1 m1 :omit-corners] [n2 m2 :omit-corners]])))

Both arrangements produce a hexagonal design, which means a four-sided rectangle contains five-pointed stars in a six-sided formation:

58 stars
79 stars