Here is the Fiddler on the Proof problem sent out on 5/22/2026 verbatim:
June the ant is on a cylinder. More specifically, she is on the edge of one of the cylinder’s two circular faces. Her dinner is on the edge of the opposite circular face, and all the way around on the other side of that face. The radius of the cylinder is 2 meters and its height is 2 meters.
Your job is to help June find the shortest path along the surface of the cylinder so that she can chow down as quickly as possible. What’s the length of this shortest path?
Let’s say this cylinder sits on the x-y-z plane defined by \(x^2+y^2 = 4\) from \(z=0\) to \(z=2\), and that the ant wants to traverse from (2,0,2) to (-2,0,0).
Let \(\theta \in [0,\pi/2]\) define the point along the edge of the top circle of the cylinder that the ant will first march to: \((2\cos(\theta), 2\sin(\theta), 2)\). The distance here uses straight line geometry and is \(\sqrt{8 - 8*\cos(\theta)}\).
Let \(\phi \in [\pi/2, \pi]\) define the point along the bottom of the cylinder that the and marches to next: \((2\cos(\phi), 2\sin(\phi), 0)\). The distance here is the straight line along the surface. It is best to imagine unrolling the outside cylinder to a rectangle so that the distance can be visualized as the hypotenuse of a triangle with legs 2 and \(2*(\phi-\theta)\), which is \(\sqrt{4 + 4*(\phi - \theta)^2}\).
Finally, it marches to (-2,0,0). The distance uses straight line geometry again and is \(\sqrt{8+8*\cos(\phi)}\). We now have the distance function
\[ D(\theta, \phi) = \sqrt{8 - 8\cos(\theta)} + \sqrt{4 + 4(\phi - \theta)^2} + \sqrt{8+8\cos(\phi)} \] Note that for \(\theta = 0\) and \(\phi = \pi\), the ant simply marches along the outside surface. We could allow for other \(\theta\) and \(\phi\), but this restricts it to the minimum-type distances.
# Distance Function
D_solid <- function(theta, phi = NULL) {
if (is.null(phi)) {
phi <- theta[2]
theta <- theta[1]
}
sqrt(pmax(0, 8 - 8 * cos(theta))) +
sqrt(4 + 4 * (phi - theta)^2) +
sqrt(pmax(0, 8 + 8 * cos(phi)))
}
# This function allows us to check numerically
make_distance_grid <- function(Dfun, theta_lim, phi_lim, n = 101) {
theta <- seq(theta_lim[1], theta_lim[2], length.out = n)
phi <- seq(phi_lim[1], phi_lim[2], length.out = n)
# Plotly surface convention: rows are y = phi, columns are x = theta.
Z <- outer(phi, theta, function(p, t) Dfun(t, p))
pts <- expand.grid(theta = theta, phi = phi)
pts$D <- Dfun(pts$theta, pts$phi)
list(theta = theta, phi = phi, Z = Z, points = pts)
}
solid_theta_lim <- c(0,pi/2)
solid_phi_lim <- c(pi/2,pi)
solid_grid <- make_distance_grid(D_solid,
solid_theta_lim,
solid_phi_lim,
n=101)
minDist <- min(solid_grid$points$D)
solid_optim <- solid_grid$points[which(solid_grid$points$D == minDist),]
solid_optim
## theta phi D
## 1 0.000000 1.570796 6.552619
## 10201 1.570796 3.141593 6.552619
This provides a minimum distance of about 6.5526.
Here is the extra credit problem verbatim.
Now, June is on a hollowed-out cylinder, also known as a “cylindrical shell.” The shell’s outer radius is 2 meters and its inner radius is 1 meter. The shell is 2 meters tall. June is on the outer edge of one of the cylinder’s two flat faces. Her dinner is on the opposite face, and all the way around on the other end of that face.
Once again, your job is to help June find the shortest path along the surface of the shell so that she can chow down as quickly as possible. What’s the length of this shortest path?
Let’s say this cylinder is the same as above, sitting on the x-y-z plane defined by \(x^2+y^2 = 4\) from \(z=0\) to \(z=2\), with the inner hollowed-out cylinder defined by \(x^2+y^2=1\). The ant still wants to traverse from (2,0,2) to (-2,0,0).
Let \(\theta \in [0,\pi/3]\) define the point along the edge the inner cylinder that the ant will first march to: \((\cos(\theta), \sin(\theta), 2)\). The distance here uses straight line geometry and is \(\sqrt{5 - 4*\cos(\theta)}\). The radian angle \(\pi/3\) is chosen as the path to that point represents the tangent.
Let \(\phi \in [2\pi/3, \pi]\) define the point along the bottom of the inner cylinder that the and marches to next: \((\cos(\phi), \sin(\phi), 0)\). The distance here is the straight line along the inner surface. It is best to imagine unrolling the inside cylinder to a rectangle so that the distance can be visualized as the hypotenuse of a triangle with legs 2 and \((\phi-\theta)\), which is \(\sqrt{4 + (\phi - \theta)^2}\).
Finally, it marches to (-2,0,0). The distance uses straight line geometry again and is \(\sqrt{5+4*\cos(\phi)}\). We now have the distance function
\[ D(\theta, \phi) = \sqrt{5 - 4\cos(\theta)} + \sqrt{4 + (\phi - \theta)^2} + \sqrt{5+4\cos(\phi)} \]
D_hollow <- function(theta, phi = NULL) {
if (is.null(phi)) {
phi <- theta[2]
theta <- theta[1]
}
sqrt(pmax(0, 5 - 4 * cos(theta))) +
sqrt(4 + (phi - theta)^2) +
sqrt(pmax(0, 5 + 4 * cos(phi)))
}
hollow_theta_lim <- c(0, pi/3)
hollow_phi_lim <- c(2*pi/3, pi)
hollow_grid <- make_distance_grid(D_hollow,
hollow_theta_lim,
hollow_phi_lim,
101)
minHollowDist <- min(hollow_grid$points$D)
hollow_optim <- hollow_grid$points[which(hollow_grid$points$D == minHollowDist),]
hollow_optim
## theta phi D
## 5701 0.4607669 2.680826 5.368971
This seems to have a minimum distance of about 5.369.
Although I did not use AI with help in finding the solution to this problem, I did want to use it to build a very cool and dynamic graph. The code that it produced is below. It may not render nicely to html, but if you can run the code in R and use it, please feel free.
clamp <- function(x, lo, hi) pmin(pmax(x, lo), hi)
# -----------------------------
# Geometry helpers
# -----------------------------
line_segment <- function(p0, p1, n = 40) {
data.frame(
x = seq(p0[1], p1[1], length.out = n),
y = seq(p0[2], p1[2], length.out = n),
z = seq(p0[3], p1[3], length.out = n)
)
}
helix_segment <- function(radius, theta, phi, z0 = 2, z1 = 0, n = 160) {
a <- seq(theta, phi, length.out = n)
data.frame(
x = radius * cos(a),
y = radius * sin(a),
z = seq(z0, z1, length.out = n)
)
}
cylinder_surface <- function(radius, zlim = c(0, 2), n_angle = 90, n_z = 30) {
a <- seq(0, 2 * pi, length.out = n_angle)
z <- seq(zlim[1], zlim[2], length.out = n_z)
A <- matrix(rep(a, each = length(z)), nrow = length(z), ncol = length(a))
Z <- matrix(rep(z, times = length(a)), nrow = length(z), ncol = length(a))
list(x = radius * cos(A), y = radius * sin(A), z = Z)
}
annulus_surface <- function(r_inner, r_outer, z_value, n_radius = 25, n_angle = 90) {
r <- seq(r_inner, r_outer, length.out = n_radius)
a <- seq(0, 2 * pi, length.out = n_angle)
R <- matrix(rep(r, times = length(a)), nrow = length(r), ncol = length(a))
A <- matrix(rep(a, each = length(r)), nrow = length(r), ncol = length(a))
list(x = R * cos(A), y = R * sin(A), z = matrix(z_value, nrow = length(r), ncol = length(a)))
}
disk_surface <- function(radius, z_value, n_radius = 25, n_angle = 90) {
annulus_surface(0, radius, z_value, n_radius, n_angle)
}
common_scene <- function(title) {
list(
aspectmode = "data",
xaxis = list(title = "x", range = c(-2.35, 2.35)),
yaxis = list(title = "y", range = c(-2.35, 2.35)),
zaxis = list(title = "z", range = c(-0.10, 2.10)),
camera = list(eye = list(x = 1.6, y = -1.9, z = 1.25)),
annotations = list(
list(text = title, showarrow = FALSE, x = 0, y = 0, z = 2.35)
)
)
}
plot_solid_geometry <- function(theta, phi) {
side <- cylinder_surface(2)
top <- disk_surface(2, 2)
bottom <- disk_surface(2, 0)
p1 <- line_segment(c(2, 0, 2), c(2 * cos(theta), 2 * sin(theta), 2))
p2 <- helix_segment(2, theta, phi, 2, 0)
p3 <- line_segment(c(2 * cos(phi), 2 * sin(phi), 0), c(-2, 0, 0))
path <- rbind(p1, p2, p3)
plot_ly() |>
add_surface(x = side$x, y = side$y, z = side$z,
opacity = 0.25, showscale = FALSE, hoverinfo = "skip",
colorscale = list(c(0, "rgb(173,216,230)"), c(1, "rgb(173,216,230)")),
name = "outer cylinder") |>
add_surface(x = top$x, y = top$y, z = top$z,
opacity = 0.18, showscale = FALSE, hoverinfo = "skip",
colorscale = list(c(0, "rgb(220,220,220)"), c(1, "rgb(220,220,220)")),
name = "top cap") |>
add_surface(x = bottom$x, y = bottom$y, z = bottom$z,
opacity = 0.18, showscale = FALSE, hoverinfo = "skip",
colorscale = list(c(0, "rgb(220,220,220)"), c(1, "rgb(220,220,220)")),
name = "bottom cap") |>
add_trace(data = path, x = ~x, y = ~y, z = ~z,
type = "scatter3d", mode = "lines",
line = list(width = 8, color = "red"),
name = "selected path",
hovertemplate = "x=%{x:.3f}<br>y=%{y:.3f}<br>z=%{z:.3f}<extra></extra>") |>
add_trace(x = c(2), y = c(0), z = c(2), type = "scatter3d", mode = "markers",
marker = list(size = 6, color = "green"), name = "start") |>
add_trace(x = c(-2), y = c(0), z = c(0), type = "scatter3d", mode = "markers",
marker = list(size = 6, color = "black"), name = "end") |>
layout(scene = common_scene("solid cylinder"), margin = list(l = 0, r = 0, b = 0, t = 25))
}
plot_hollow_geometry <- function(theta, phi) {
outer <- cylinder_surface(2)
inner <- cylinder_surface(1)
top <- annulus_surface(1, 2, 2)
bottom <- annulus_surface(1, 2, 0)
p1 <- line_segment(c(2, 0, 2), c(cos(theta), sin(theta), 2))
p2 <- helix_segment(1, theta, phi, 2, 0)
p3 <- line_segment(c(cos(phi), sin(phi), 0), c(-2, 0, 0))
path <- rbind(p1, p2, p3)
plot_ly() |>
add_surface(x = outer$x, y = outer$y, z = outer$z,
opacity = 0.12, showscale = FALSE, hoverinfo = "skip",
colorscale = list(c(0, "rgb(173,216,230)"), c(1, "rgb(173,216,230)")),
name = "outer wall") |>
add_surface(x = inner$x, y = inner$y, z = inner$z,
opacity = 0.33, showscale = FALSE, hoverinfo = "skip",
colorscale = list(c(0, "rgb(255,228,181)"), c(1, "rgb(255,228,181)")),
name = "inner wall") |>
add_surface(x = top$x, y = top$y, z = top$z,
opacity = 0.22, showscale = FALSE, hoverinfo = "skip",
colorscale = list(c(0, "rgb(220,220,220)"), c(1, "rgb(220,220,220)")),
name = "top annulus") |>
add_surface(x = bottom$x, y = bottom$y, z = bottom$z,
opacity = 0.22, showscale = FALSE, hoverinfo = "skip",
colorscale = list(c(0, "rgb(220,220,220)"), c(1, "rgb(220,220,220)")),
name = "bottom annulus") |>
add_trace(data = path, x = ~x, y = ~y, z = ~z,
type = "scatter3d", mode = "lines",
line = list(width = 8, color = "red"),
name = "selected path",
hovertemplate = "x=%{x:.3f}<br>y=%{y:.3f}<br>z=%{z:.3f}<extra></extra>") |>
add_trace(x = c(2), y = c(0), z = c(2), type = "scatter3d", mode = "markers",
marker = list(size = 6, color = "green"), name = "start") |>
add_trace(x = c(-2), y = c(0), z = c(0), type = "scatter3d", mode = "markers",
marker = list(size = 6, color = "black"), name = "end") |>
layout(scene = common_scene("hollow cylinder"), margin = list(l = 0, r = 0, b = 0, t = 25))
}
plot_distance_surface <- function(grid, Dfun, theta, phi, title, source_id) {
selected_D <- Dfun(theta, phi)
p <- plot_ly(source = source_id) |>
add_surface(x = grid$theta, y = grid$phi, z = grid$Z,
opacity = 0.88, showscale = TRUE,
colorscale = "Viridis",
colorbar = list(title = "D"),
name = "D(theta, phi)",
hovertemplate = paste(
"theta=%{x:.5f}<br>",
"phi=%{y:.5f}<br>",
"D=%{z:.7f}<extra></extra>"
)) |>
# Thin scatter3d overlay makes clicking/selecting grid points reliable.
add_trace(data = grid$points,
x = ~theta, y = ~phi, z = ~D,
type = "scatter3d", mode = "markers",
marker = list(size = 1.4, opacity = 0.13, color = "black"),
name = "clickable grid",
hovertemplate = paste(
"theta=%{x:.5f}<br>",
"phi=%{y:.5f}<br>",
"D=%{z:.7f}<extra></extra>"
)) |>
add_trace(x = c(theta), y = c(phi), z = c(selected_D),
type = "scatter3d", mode = "markers",
marker = list(size = 6, color = "red"),
name = "selected") |>
layout(
title = title,
scene = list(
xaxis = list(title = "theta"),
yaxis = list(title = "phi"),
zaxis = list(title = "distance D"),
camera = list(eye = list(x = 1.5, y = -1.7, z = 1.1))
),
margin = list(l = 0, r = 0, b = 0, t = 40)
)
event_register(p, "plotly_click")
}
selected_text <- function(theta, phi, Dval) {
paste0(
"theta = ", sprintf("%.8f", theta), "\n",
"phi = ", sprintf("%.8f", phi), "\n",
"D = ", sprintf("%.10f", Dval)
)
}
extract_click <- function(ed, theta_lim, phi_lim) {
if (is.null(ed) || nrow(ed) < 1) return(NULL)
theta <- suppressWarnings(as.numeric(ed$x[1]))
phi <- suppressWarnings(as.numeric(ed$y[1]))
if (!is.finite(theta) || !is.finite(phi)) return(NULL)
c(
theta = clamp(theta, theta_lim[1], theta_lim[2]),
phi = clamp(phi, phi_lim[1], phi_lim[2])
)
}
solid_grid <- make_distance_grid(D_solid, solid_theta_lim, solid_phi_lim, n = 101)
hollow_grid <- make_distance_grid(D_hollow, hollow_theta_lim, hollow_phi_lim, n = 101)
# -----------------------------
# Shiny app
# -----------------------------
ui <- fluidPage(
titlePanel("Shortest paths on solid and hollow cylinders"),
p("Click a point on a distance surface, or adjust the angle sliders. The path on the cylinder updates to match the selected theta and phi."),
tabsetPanel(
tabPanel(
"Solid cylinder",
sidebarLayout(
sidebarPanel(
h4("Selected point"),
sliderInput("solid_theta", "theta", min = 0, max = pi / 2, value = 0,
step = 0.001),
sliderInput("solid_phi", "phi", min = pi / 2, max = pi, value = pi / 2,
step = 0.001),
verbatimTextOutput("solid_selected"),
tags$hr(),
h4("Numerical check"),
verbatimTextOutput("solid_min_check")
),
mainPanel(
fluidRow(
column(6, plotlyOutput("solid_distance", height = "650px")),
column(6, plotlyOutput("solid_geometry", height = "650px"))
)
)
)
),
tabPanel(
"Hollow cylinder",
sidebarLayout(
sidebarPanel(
h4("Selected point"),
sliderInput("hollow_theta", "theta", min = 0, max = pi / 3,
value = hollow_optim$theta, step = 0.001),
sliderInput("hollow_phi", "phi", min = 2 * pi / 3, max = pi,
value = hollow_optim$phi, step = 0.001),
verbatimTextOutput("hollow_selected"),
tags$hr(),
h4("Numerical check"),
verbatimTextOutput("hollow_min_check")
),
mainPanel(
fluidRow(
column(6, plotlyOutput("hollow_distance", height = "650px")),
column(6, plotlyOutput("hollow_geometry", height = "650px"))
)
)
)
)
)
)
server <- function(input, output, session) {
observeEvent(event_data("plotly_click", source = "solid_dist", priority = "event"), {
xy <- extract_click(event_data("plotly_click", source = "solid_dist", priority = "event"),
solid_theta_lim, solid_phi_lim)
if (!is.null(xy)) {
updateSliderInput(session, "solid_theta", value = xy["theta"])
updateSliderInput(session, "solid_phi", value = xy["phi"])
}
}, ignoreInit = TRUE)
observeEvent(event_data("plotly_click", source = "hollow_dist", priority = "event"), {
xy <- extract_click(event_data("plotly_click", source = "hollow_dist", priority = "event"),
hollow_theta_lim, hollow_phi_lim)
if (!is.null(xy)) {
updateSliderInput(session, "hollow_theta", value = xy["theta"])
updateSliderInput(session, "hollow_phi", value = xy["phi"])
}
}, ignoreInit = TRUE)
output$solid_distance <- renderPlotly({
plot_distance_surface(
solid_grid, D_solid,
input$solid_theta, input$solid_phi,
"Solid model: D(theta, phi)",
"solid_dist"
)
})
output$solid_geometry <- renderPlotly({
plot_solid_geometry(input$solid_theta, input$solid_phi)
})
output$solid_selected <- renderText({
selected_text(input$solid_theta, input$solid_phi,
D_solid(input$solid_theta, input$solid_phi))
})
output$solid_min_check <- renderText({
paste0(
"Expected equal minima:\n",
" theta = 0, phi = pi/2, D = ", sprintf("%.10f", D_solid(0, pi / 2)), "\n",
" theta = pi/2, phi = pi, D = ", sprintf("%.10f", D_solid(pi / 2, pi)), "\n\n",
"Best multi-start optim result:\n",
" theta = ", sprintf("%.8f", solid_optim$theta[1]), "\n",
" phi = ", sprintf("%.8f", solid_optim$phi[1]), "\n",
" D = ", sprintf("%.10f", solid_optim$D[1])
)
})
output$hollow_distance <- renderPlotly({
plot_distance_surface(
hollow_grid, D_hollow,
input$hollow_theta, input$hollow_phi,
"Hollow model: D(theta, phi)",
"hollow_dist"
)
})
output$hollow_geometry <- renderPlotly({
plot_hollow_geometry(input$hollow_theta, input$hollow_phi)
})
output$hollow_selected <- renderText({
selected_text(input$hollow_theta, input$hollow_phi,
D_hollow(input$hollow_theta, input$hollow_phi))
})
output$hollow_min_check <- renderText({
paste0(
"Symmetry-reduced optimum:\n",
" theta = ", sprintf("%.8f", hollow_optim$theta), "\n",
" phi = ", sprintf("%.8f", hollow_optim$phi), "\n",
" D = ", sprintf("%.10f", hollow_optim$D), "\n\n",
"Best multi-start optim result:\n",
" theta = ", sprintf("%.8f", hollow_optim$theta[1]), "\n",
" phi = ", sprintf("%.8f", hollow_optim$phi[1]), "\n",
" D = ", sprintf("%.10f", hollow_optim$D[1])
)
})
}
shinyApp(ui, server)