exupero 2023-09-28T12:27:47+00:00 https://blog.exupero.org/ exupero https://blog.exupero.org/circumcenters-in-taxicab-geometry Circumcenters in taxicab geometry 2023-08-28T23:59:59+00:00 Circumcenters in taxicab geometry

Besides conic sections, there is one other kind of shape we can define using distance: perpendiculars. A perpendicular line is a line at a 90° angle to some reference line, but in Euclidean geometry, that's also the set of points equidistant from two points. However, rather than redefine the word perpendicular to be about distance instead of angle, I'll call the set of equidistant points the midset.

We've actually already seen what a midset looks like in taxicab geometry: it's a hyperbola with zero difference in distance between its foci. Here's one example:

One use for midsets is to find the point that's equidistant from each point of a triangle. To do so, we draw any two midsets; their intersection will be the same distance from all three corners. Here's a triangle and all three of its midsets:

To show the intersection is equidistant from all three points, we can draw a circle:

The intersection of a triangle's midsets is the circumcenter. Pleasingly, the algorithm to find it is exactly the same in taxicab geometry as in Euclidean geometry, though with the distinction that we're using midsets instead of 90° perpendicular lines.

You may recall from the discussion of hyperbolas, however, that there's a special case to check, namely when the horizontal spacing between two points equals the vertical spacing and the midset's ends flare out into areas:

Here's a triangle with a side at 45° to the axes:

One side's midset passes through the area of another side's midset. The circumcenter is now a line segment instead of a point. This happens because, with one side at 45°, two corners of the triangle fall on one side of the circumcircle and we can slide the center of the circumcircle along the intersection keeps and still keep both corners on the circumcircle.

Note that it still doesn't matter which two midsets we draw, the intersection of any two midsets are the same:

What if two sides are at 45°?

The intersection of two midsets is now a ray, in the above case, extending left from the center of the triangle's hypotenuse. This makes sense, since a triangle with two sides at 45° to the axes is one corner of a taxicab circle, no matter how big the circle is.

The same is true—but harder to see—with the midsets of the two 45° sides, which produces a one-dimensional intersection of two areas:

]]>
https://blog.exupero.org/angles-in-taxicab-geometry Angles in taxicab geometry 2023-08-31T23:59:59+00:00 Angles in taxicab geometry

In the previous post I showed how to construct a triangle's circumcenter using taxicab geometry's version of a perpendicular line, the midset. There's another triangle center, but before we discuss it we have to explore angle measurement in taxicab geometry, which behaves very differently than it does in Euclidean geometry.

To illustrate, consider a circle with two 30° angles, one from 0° to 30°, the other from 30° to 60°:

Notice the arc lengths within the angles: the angle from 0° to 30° spans an arc that's almost 6 (Manhattan) units long, while the angle from 30° to 60° is just over 4 units long. The size of an angle in taxicab geometry, then, depends on how the angle is oriented to the axes. The biggest 1° angles are in the cardinal directions:

One degree of a Euclidean circle spans 1/360th of the circumference, but each of the above 1° angles spans about 1/230th of the taxicab circle's circumference, about one and a half times bigger.

The smallest 1° angles in taxicab geometry are perpendicular to the sides of a circle, at 45° angles to the axes:

These 1° angles span only about 1/460th of the circle, approximately three-quarters of a Euclidean degree.

Here are close-ups of the biggest and smallest 1° angles (on a finer grid):

The east-facing 1° angle spans an arc of almost 8 squares on the above grid, while the angle facing northeast only spans an arc of 4 squares, half as long.

These close-ups help us see what causes the difference. At a 45° angle from the center, a radius meets a circle at 90°, and the circle's perimeter traverses close to the shortest possible distance between the two radii. When a radius falls on the horizontal or vertical, however, it meets the circle at 45°, and the perimeter cuts at 45°, taking a much longer diagonal between two radii.

Thus, in taxicab geometry, if one degree means an angle that spans 1/360th of a circle's circumference, then 1° on the horizontal and vertical has to be narrower, and 1° on diagonals has to be wider. In the case of a unit taxicab circle, whose circumference is 8 units, 1° spans 8/360 units = 1/45 units = 0.02 units.

]]>
https://blog.exupero.org/angle-bisection-in-taxicab-geometry Angle bisection in taxicab geometry 2023-09-04T23:59:59+00:00 Angle bisection in taxicab geometry

In the previous post we saw how angles in taxicab geometry vary in size depending on their direction. Given that behavior, how can one bisect angle?

In Euclidean geometry, an angle can be bisected with a compass and straightedge. If we do the same construction with taxicab geometry's circles, we can get something like this:

The smaller circles centered on the rays have an infinite number of points of intersection, rather than just two, and it's not clear where to draw the angle bisector. When the angle does allow ray-circles to have only two points of intersection, those two points max not align with the vertex of the angle, as in this example:

A line from the angle's vertex to the circle intersection closest to it clearly wouldn't produce an angle bisector, but would a line from the vertex to the far intersection? That's not so obvious.

The most reliable way I've found to get an angle bisector in taxicab geometry is to find the intersection of lines parallel to an angle's rays. Here are two parallel lines:

And here's a line from the vertex to their intersection, which is very plausibly a bisector:

As a check of whether this does produce a bisector, let's use parallel lines to find a triangle's incenter. Here's an arbitrary triangle and three interior parallel lines:

And here are the angle bisectors suggested by the intersections of the parallel lines:

The first indication that we're in luck is that all three bisectors intersect at the same point. A circle centered on that point and given the correct radius does appear to touch all three sides of the triangle, as an incircle should:

This is hardly proof, however, especially given taxicab geometry's proclivity for edge cases. The triangle above is acute; let's try an obtuse triangle:

So far, so good. For my purposes, I don't need a proof, but if you have a counter-example or edge case to consider, let me know.

]]>
https://blog.exupero.org/parallel-line-construction-in-taxicab-geometry Parallel line construction in taxicab geometry 2023-09-07T23:59:59+00:00 Parallel line construction in taxicab geometry

In the previous post I glossed over one step in bisecting taxicab angles: the construction of parallel lines. We can make such lines in either of two ways: using Euclidean compass and straightedge construction, or using taxicab circles.

Parallel lines in taxicab geometry aren't any different than in Euclidean geometry, only how the distance between the lines is measured. Thus, if we have a point the right distance from the line, we can use any regular compass and straightedge construction that produces a parallel line through a point. For lines angled closer to the horizontal axis, pick a point the desired distance above or below the line; for lines closer to the vertical, pick a point left or right of the line.

If we use taxicab circles, however, there's a shortcut. We only need to draw two circles of the same radius centered on the line, like this:

We can construct parallel lines by placing a straightedge against the corners of the circles:

Technically this construction works with Euclidean circles too, but it has the disadvantage that the straightedge can't be placed on any established points and its placement tangent to the circles has to be eyeballed. A book I have on compass and straightedge construction calls it a "cheat". In taxicab geometry, however, the corners of the circles serve as established points.

The trick now is how to reliably draw a taxicab circle.

]]>
https://blog.exupero.org/geojson-io Geojson.io 2023-09-11T23:59:59+00:00 Geojson.io

Geojson.io is a simple tool that does its job well. As you draw shapes and place pins on a world map it produces GeoJSON code that you can edit and copy to save elsewhere. You can paste in GeoJSON to see its shapes drawn, as well as export to different file formats.

Mostly I use geojson.io to trace routes of hikes, bicycle rides, and car trips, both for measuring distance as well as for a personal archive.

This blog has prompted a couple uses. Once I wanted to trace a path on a reference image and get its geographic coordinates. I didn't find any metadata on the map about what projection it used, so on geojson.io I loosely traced Africa and Greenland (an equatorial landmass and a polar landmass) and projected the coordinates using different formulas to see how well my outlines aligned with Africa and Greenland on the reference image.

Another time I narrowed something down to a particular range of latitudes, drew a bounding box on the globe using geojson.io, and from that narrowed down the likely range of longitudes. I took a screenshot of resulting rectangle and included in the post, though I could also have saved the GeoJSON data to a GitHub Gist and embedded it, as I did when mapping Michigan county boundary roads whose names are portmanteaus.

]]>
https://blog.exupero.org/merging-geojson-features Merging GeoJSON features 2023-09-14T23:59:59+00:00 Merging GeoJSON features

In the previous post I mentioned that I track hikes, bicycle rides, and car trips in GeoJSON files. Each file is named after a particular date and contains the path of that day's activity. To see a cumulative map of everywhere I've logged hikes or bike rides or drives, I have a Babashka script called `merge-geojson-features` which combines the GeoJSON data into a single structure:

``(require '[cheshire.core :as json])``
``````(let [files *command-line-args*]
(println
(json/generate-string
{:type "FeatureCollection"
:features (sequence
(comp
(map slurp)
(map json/parse-string)
(mapcat #(get % "features")))
files)}
{:pretty (json/create-pretty-printer
(assoc json/default-pretty-print-options
:indent-arrays? true))})))``````

Probably jq could do the same with a one-liner, but so far I haven't figured out how. If you know, please email me.

Writing 14 lines of code in a language I'm familiar with is usually faster than writing one line in a language I only know the rudiments of, which is why I usually reach for Babashka for tasks like this. There's also the temptation to think that the script will develop further. For example, I could store the source filename as a property on each feature then be able to see when I took a particular trip in the shape info popup on geojson.io. So far, however, I haven't had the need.

The script prints the GeoJSON to the terminal, so I typically pipe it to `pbcopy` then paste the data into geojson.io to see it on a map.

]]>
https://blog.exupero.org/multiplying-the-expected-value-of-a-uniform-distribution Multiplying the expected value of a uniform distribution 2023-09-18T23:59:59+00:00 Multiplying the expected value of a uniform distribution

While teaching my kids some basics of probability, I noticed something interesting about the expected value of uniform distributions. Since the expected value is a weighted average of outcomes, it hasn't always seemed very useful to me. For example, the expected value of the roll of a six-sided die is (1 + 2 + 3 + 4 + 5 + 6) ÷ 6 = 7/2 = 3.5, which not only is never the outcome of rolling a six-sided die but doesn't convey anything about how each outcome has the same likelihood. It only gives us limited information about the range of possible outcomes.

But it does tell us something if we roll more than one die and take the sum of the individual outcomes. To illustrate, here are all possible sums when rolling two dice:

+123456
1234567
2345678
3456789
45678910
567891011
6789101112

And a table of each outcome's frequency:

OutcomeOccurrences
21
32
43
54
65
76
85
94
103
112
121

Seven is the most likely outcome. This is more or less common knowledge, especially among players of board games, but I didn't know what sums were most likely when rolling more dice. As an introductory programming exercise for my oldest kids, I had them do the above tabulation for three and four dice. Here are the results.

Using three dice, the most likely sums are 10 and 11:

OutcomeOccurrences
31
43
56
610
715
821
925
1027
1127
1225
1321
1415
1510
166
173
181

Using four dice, the most likely sum is 14:

OutcomeOccurrences
41
54
610
720
835
956
1080
11104
12125
13140
14146
15140
16125
17104
1880
1956
2035
2120
2210
234
241

In other words, while the expected value of rolling one die is 3.5 and not very informative, it does tell us that the most common sum of rolling two dice is 7/2 × 2 = 7. When rolling three dice, the most common sum is 7/2 × 3 = 21/2 = 10.5. That's not one of the possible outcomes, but the two outcomes nearest turn out to be the most common. When rolling four dice, the most common sum is 7/2 × 4 = 14.

The pattern holds for rolling five and six dice as well, predicting sums of 17 at 18 for five dice and 21 for six dice. I haven't proved anything, but a little spot-checking shows the relationship holding for die with more sides, which makes sense given we haven't done anything inherently six-sided. I don't expect the relationship to hold for non-uniform distributions.

While this seems like an easy way to mentally calculate what sums are most likely when rolling arbitrary numbers of dice, there's an easier way: simply look at the range of possible sums and find the middle. Rolling three dice, the possible sums range from 3 to 18 and have a middle of 3 + (18 - 3)÷2 = 10.5. With four dice sums range from 4 to 24 with a middle of 4 + (24 - 4)÷2 = 14.

]]>

In the previous post I showed how, when rolling a set of identical dice, we can calculate what sum is most common by multiplying the expected value of one die, or by finding the middle value between the lowest and highest possible sums. I was curious if the same techniques work when rolling dice that have different numbers of sides. It seems to, but with a complication.

Rather than rolling two six-sided dice, let's consider rolling one six-sided die and one ten-sided die. The expected values are 3.5 and 5.5, respectively, which sum to 9. Nine is also the middle value between the highest and lowest sums possible (2 and 16). And, working out the table of sums, it turns out 9 is indeed a most common sum when rolling these two dice.

But, unlike when rolling identical dice, more than one sum may be most common. When rolling six- and ten-sided dice, sums of 7, 8, 9, 10, and 11 all share equal likelihood. To see why, look at the sum table:

+12345678910
1234567891011
23456789101112
345678910111213
4567891011121314
56789101112131415
678910111213141516

The unequal dimensions of the table create more than one longest diagonal, giving the sums on those diagonals equal probability.

Something similar happens when rolling more than two dice, though it's tougher to visualize. For six-, ten-, and twenty-sided dice, the most common sums are all the integers from 15 to 22.

However, there do still exist scenarios where one sum can be more common than all others. It doesn't seem to happen at all when rolling only two dice, but when rolling three it appears to happen when exactly one die has an odd number of sides and the die with the most sides has fewer sides than the sum of the sides of the other two dice. To illustrate, here are some example dice combinations that have a single most common sum:

• 6, 7, 8
• 6, 7, 10
• 6, 7, 12
• 6, 8, 9
• 6, 8, 11
• 6, 8, 13
• 6, 9, 10
• 6, 9, 12
• 6, 9, 14
• 6, 10, 11
• 6, 10, 13
• 6, 10, 15
• 6, 11, 12
• 6, 11, 14
• 6, 11, 16
• 6, 12, 13
• 6, 12, 15
• 6, 12, 17

With four dice, it seems exactly two dice have to be odd, and the die with the most sides has to have fewer sides than the sum of the sides of the other three dice:

• 6, 7, 8, 9
• 6, 7, 8, 11
• 6, 7, 8, 13
• 6, 7, 8, 15
• 6, 7, 8, 17
• 6, 7, 8, 19
• 6, 7, 9, 10
• 6, 7, 9, 12
• 6, 7, 9, 14
• 6, 7, 9, 16
• 6, 7, 9, 18
• 6, 7, 9, 20
• 6, 7, 10, 11
• 6, 7, 10, 13
• 6, 7, 10, 15
• 6, 7, 10, 17
• 6, 7, 10, 19
• 6, 7, 10, 21

Or at least, that's how it looks. I haven't probed the math to see if those constraints are inherent or simply an artifact of the combinations I happened to generate. If you have any insight on this behavior, feel free to email me.

]]>
https://blog.exupero.org/penny-game The Penny Game 2023-09-25T23:59:59+00:00 The Penny Game

In his book The Goal, Eliyahu Goldratt described a simulation of a manufacturing line using match sticks and dice, though I believe one of his later books used pennies instead of match sticks. I've called the simulation "the penny game" for years, so I'll stick with that term for this series.

The simulation, in brief, is that we have a number of sequential stations on a manufacturing line, and each station's capacity to move pennies to the next station isn't fixed but is determined by a roll of a die. Our job is to optimize the line. To facilitate experimentation, we'll run simulations with code rather than rolling dice and moving pennies manually.

We'll represent our manufacturing line as a list of numbers. Each position in the list is a station, and each number is how many pennies the respective station has to work on:

``````(def initial-state
{:stations (repeat 6 4)})``````
``````{:stations (4 4 4 4 4 4)}
``````

As with any simulation, we need function to update the simulation's state. Ours will take the previous state and a list indicating how many pennies each station can process. The number of pennies actually processed, however, depends not only on the station's production capacity but also on how many pennies it has in its queue; no station can process more pennies than it has to work on. The output of each station is added to the next station's queue. Here's the update function:

``````(defn update-state [{:keys [stations]} [incoming & capacities]]
(let [processed (map min stations capacities)
new-stations (map (fn [queue outgoing incoming]
(-> queue
(- outgoing)
(+ incoming)))
stations
processed
(cons incoming processed))]
{:incoming incoming
:capacities capacities
:processed processed
:stations new-stations}))``````

Notice that the second argument to this function, representing station productivity, has an extra value prepended to the beginning of it. That value represents how many pennies are added to the first station's queue, which is why it's `cons`'d onto the list of processed pennies, to be used as input to the first station.

I've included the number of incoming pennies, the capacities of each station, and how many pennies each station processed so we can later check the logic and do some analysis.

We can simulate the manufacturing line using `iterate` and a random number generator:

``````(defn simulate
[initial-state update-state iterations generate-capacities]
(let [rng (java.util.Random. 10)
roll #(.nextInt rng 1 7)]
(take (inc iterations)
(iterate #(update-state % (generate-capacities roll))
initial-state))))``````

We increment the number of iterations because `iterate` produces a sequence whose first item is our initial state.

To simulate the line, we provide our initial state, a number of iterations to run, and a function which provides a list of the incoming pennies and how many pennies each station is capable of processing in this step:

``````(simulate initial-state update-state 1
(fn [roll]
(repeatedly 7 roll)))``````
``````({:stations (4 4 4 4 4 4)}
{:incoming 4,
:capacities (1 4 1 5 5 2),
:processed (1 4 1 4 4 2),
:stations (7 1 7 1 4 6)})
``````

We can check the results: the first station had 4 pennies, could only proccess 1, then it received 4 from the pennies coming into the line, so it now has 7 pennies. The second station had 4 pennies, processed 4, then received 1 from the previous station's output, giving it 1 penny. And so on.

Since we're trying to optimize the line, let's run the simulation for 100 steps and see the total output of the final station, which gives us the overall production of the entire line:

``````(defn total-output [steps]
(transduce
(map #(-> % :processed last (or 0)))
+ steps))``````
``````(total-output
(simulate initial-state update-state 100
(fn [roll]
(repeatedly 7 roll))))``````
``````290
``````

In the next post, we'll look at a metric in additional to total output that we should consider when optimizing our line.

]]>
https://blog.exupero.org/penny-game-work-in-progress Penny game work in progress 2023-09-28T23:59:59+00:00 Penny game work in progress

In the previous post I created a basic simulation of a manufacturing line. We ran the line for 100 iterations and looked at the total output. Total output gives us an indication of the line's revenue. For us, the line's output was:

``````(def steps
(simulate initial-state update-state 100
(fn [roll]
(repeatedly 7 roll))))``````
``(total-output steps)``
``````290
``````

But a manufacturing line also has expenses, among them its material input:

``````(defn total-input [steps]
(transduce
(map #(-> % :incoming (or 0)))
+ steps))``````
``(total-input steps)``
``````337
``````

The difference between the number of pennies that have gone into the line and the number of pennies that have come out is a result of how many pennies are currently being worked on by the line. We can see how much work in progress there is for each step. At the beginning, there are 24 pennies in progress:

``````(defn work-in-progress [step]
(reduce + (step :stations)))``````
``(work-in-progress (first steps))``
``````24
``````

At the end, however, there are quite a few more:

``(work-in-progress (last steps))``
``````71
``````

If we graph the amount of work in progress for each step, we can see it growing:

Work in progress is another expense faced by a manufacturing line; we need somewhere to store all that material, as well as people and equipment to move it around. Thus, if we want to optimize our line, we should not only maximize output but minimize work in progress. We'll experiment with more productive stations in the next post.

]]>