Can you Catch the Longest Wave

The 6/12/2026 Fiddler on the Proof problem is given below verbatim:

Semicircle Island is shaped like a perfect semicircle (or semidisk, technically), with a radius of 1 mile. It doesn’t have any permanent residents, but it’s a very popular destination for surfers.

Rumor has it that a big wave is headed toward the island, but no one knows which direction it’s coming from. This thin, straight wall of water never changes speed or direction. It will first make contact with the island at 10 a.m. and it will last be in contact with the island at 10:10 a.m.

What is the longest possible stretch of land that is directly under the wave at 10:05 a.m.?

Solution

First, I built a semicircle in the plane with radius 1 mile using \(y=\sqrt{1-x^2}\) and the line \(y=0\) between \(x=-1\) and \(x=1\).

The wave is defined by \(\theta\) (see diagram). Note that this intersects the semi-circle on the border at \((\cos(\theta), \sin(\theta))\). Thus, the slope of the line defined by the wave is \(m = \tan(\theta)\).

Figure 1

The line with slope \(m\) that intersects the point (1,0) is \(y = mx - m\), and so the intercept of the wave that either first or last touches the island at the bottom is \(b_{\text{low}} = -m = -\tan(\theta)\).

Notice that the line perpendicular to our wave defined by \(\theta\) is \(\theta+\frac{\pi}{2}\), and the point on the semi-circle defined by this is \((\cos(\theta+\pi/2), \sin(\theta+\pi/2)) = (-\sin(\theta), \cos(\theta))\). Now, the wave line that goes through this point (first or last) with slope \(m\) has the form \(y = mx + (\cos(\theta)+m\sin(\theta))\). The intercept of this wave is \(b_{\text{high}} = (\cos(\theta)+m\sin(\theta))\).

Therefore, the intercept of the line in the middle of these two waves is \(b_{\text{mid}} = \frac12(\cos(\theta) + m \sin(\theta)-\tan(\theta))\), which simplifies to \(b_{\text{mid}} = \frac{1-\sin(\theta)}{2\cos(\theta)}\).

As we can notice from the diagram, the mid-length of the wave for a specific \(\theta\) will be a step-function that changes when that mid-wave is at (-1,0). That mid-wave line has the form \(y=mx+b_{\text{mid}}\). So, it will pass through that point when \(0 = -m + b_{\text{mid}}\), or when \(m = b_\text{mid}\).

This means we solve \(\tan(\theta) = \frac{1-\sin(\theta)}{2\cos(\theta)}\) for \(\theta\). This can be reduced to \(\sin(\theta) = 1/3\), which means that \(\theta = \arcsin(1/3) \approx 0.3398369\).

From the diagram, we can deduce that when \(\theta < \arcsin(1/3)\) the mid-wave will intersect semi-circle, and when \(\theta > \arcsin(1/3)\) it will intersect the line \(y=0\). It is a reasonable guess that the mid-wave that intersects exactly both at (-1,0) is probably the longest.

Let’s find where the line \(y = mx+b_{\text{mid}}\) intersects the curve \(y=\sqrt{1-x^2}\). Setting the right sides equal to one another and solving, we find that we need to solve the quadratic equation \(Ax^2 + Bx + C = 0\) when \(A = m^2+1\), \(B=2mb_\text{mid}\), and \(C = b_\text{mid}^2 -1\).

When \(\theta = \arcsin(1/3)\), we have \(\sin(\theta) = 1/3\), \(\cos(\theta) = \sqrt{8}/3\), and \(\tan(\theta) = 1/\sqrt{8}\).

This leads to \(A = 9/8\), \(B=1/4\) and \(C = -7/8\). The quadratic formula produces \(x = 7/9\) and \(x = -1\) as solutions. Thus, we need to find the distance between the points \(\left(\frac79, \frac{4\sqrt{2}}{9}\right)\) and \((-1,0)\).

The distance between these points is \(\sqrt{(4\sqrt{2}/9)^2 + (7/9 +1)^2} = 4\sqrt{2}/3 \approx 1.88562\), which is a reasonable guess for the longest distance.

Let’s take a quick break and build a function using what we’ve learned.

midLen <- function(theta){
    m <- tan(theta)
    bmid <- (1-sin(theta))/(2*cos(theta))
    
    A <- m^2+1
    B <- 2*m*bmid
    C <- bmid^2 - 1
    x1 <- (-B + sqrt(B^2 - 4*A*C))/(2*A)
    x2_1 <- (-B - sqrt(B^2 - 4*A*C))/(2*A)
    x2_2 <- -bmid/m 
    
    res <- numeric(length(theta))
    less <- x2_2 < -1
    
    res[!less] <- sqrt(1-x1[!less]^2 + (x1[!less]-x2_2[!less])^2)
    
    res[less] <- sqrt((sqrt(1-x1[less]^2)-
                         sqrt(1-x2_1[less]^2))^2+(x1[less]-x2_1[less])^2)
    
    res
}

# Look on both sides of arcsin(1/3)
midLen(c(asin(1/3)-1e-5, asin(1/3), asin(1/3)+1e-5))
## [1] 1.885615 1.885618 1.885576

There seems to be a maximum at \(\theta = \arcsin(1/3)\). Let’s graph it to make sure.

curve(midLen(x), from = 0, to = pi,
      xlab = expression(theta),
      ylab = "midLen(theta)")

abline(v = asin(1/3), col = "red", lty = 2)
points(asin(1/3), 4*sqrt(2)/3, pch = 19, col = "red")

Extra Credit

Here was the Fiddler on the Proof extra credit given without the diagram or reference to the diagram. (Just see above figure for a good idea).

Another wave is approaching the island, but again no one knows which direction it’s coming from—for the moment, all directions are equally likely.

On average, what is the length of the stretch of land directly under the wave halfway between when the wave first and last makes contact with the island?

Solution to Extra Credit

We’ve done most the work above. Let’s use the defined function above and find the mean value of it over 0 to \(\pi\) using the mean value theorem. That \(f_\text{ave} = \frac{1}{b-a} \int_a^b f(x) dx\). There is an integrate function for R, so I’ll use that below. We’ll use the fact that it is symmetrical from \(\pi\) to \(2\pi\).

avgMidLen <- 2/pi * integrate(midLen, lower = 0, upper = pi/2)$value
avgMidLen
## [1] 1.304433

Thus, the average length of the stretch of land directly under the wave halfway between when the wave first and last makes contact with the island is about 1.304433.

Video for Possible Best Picture Award

Here is a gif that you are free to share and use how you’d like, and the R code below that produced it.

library(ggplot2)
library(gganimate)

fps <- 30
duration <- 18
pause_seconds <- 3

theta_star <- asin(1 / 3)
theta_end <- pi / 2

n_total <- fps * duration
n_pause <- fps * pause_seconds
n_move <- n_total - n_pause

n_before <- round(n_move * theta_star / theta_end)
n_after <- n_move - n_before

theta_vals <- c(
  seq(0, theta_star, length.out = n_before),
  rep(theta_star, n_pause),
  seq(theta_star, theta_end, length.out = n_after)
)

frames <- seq_along(theta_vals)
target_max_length <- 1.885618

circle <- data.frame(
  x = seq(-1, 1, length.out = 700)
)
circle$y <- sqrt(1 - circle$x^2)

make_frame <- function(theta, frame) {
  s <- sin(theta)
  c0 <- cos(theta)

  # Normal form for slope tan(theta):
  # -sin(theta) * x + cos(theta) * y = intercept
  n <- c(-s, c0)
  u <- c(c0, s)

  c_low <- -s
  c_mid <- (1 - s) / 2
  c_high <- 1

  t_full <- seq(-2, 2, length.out = 300)

  line_points <- function(c_line, name) {
    p0 <- c_line * n
    data.frame(
      frame = frame,
      theta = theta,
      line = name,
      x = p0[1] + t_full * u[1],
      y = p0[2] + t_full * u[2]
    )
  }

  lower <- line_points(c_low, "b_low")
  middle <- line_points(c_mid, "b_mid")
  upper <- line_points(c_high, "b_high")

  # Endpoints of the mid-line segment inside the upper semicircle.
  p0 <- c_mid * n
  t_circle <- sqrt(1 - c_mid^2)

  if (abs(s) < 1e-10) {
    t_lower <- -t_circle
  } else {
    t_axis <- -p0[2] / s
    t_lower <- max(-t_circle, t_axis)
  }

  t_upper <- t_circle

  x1 <- p0[1] + t_lower * u[1]
  y1 <- p0[2] + t_lower * u[2]
  x2 <- p0[1] + t_upper * u[1]
  y2 <- p0[2] + t_upper * u[2]

  actual_length <- t_upper - t_lower
  shown_length <- min(actual_length, target_max_length)

  mid_inside <- data.frame(
    frame = frame,
    theta = theta,
    x = x1,
    y = y1,
    xend = x2,
    yend = y2,
    length_actual = actual_length,
    length_shown = shown_length
  )

  theta_vec <- data.frame(
    frame = frame,
    theta = theta,
    x = 0,
    y = 0,
    xend = cos(theta),
    yend = sin(theta)
  )

  intercepts <- data.frame(
    frame = frame,
    theta = theta,
    label = c("b[low]", "b[mid]", "b[high]"),
    x = c(0.04, 0.04, 0.04),
    y = c(c_low, c_mid, c_high)
  )

  length_label <- data.frame(
    frame = frame,
    theta = theta,
    x = -1.18,
    y = 1.2,
    label = sprintf("mid-line length: %.6f", shown_length)
  )

  list(
    lines = rbind(lower, upper),
    middle = middle,
    mid_inside = mid_inside,
    theta_vec = theta_vec,
    intercepts = intercepts,
    length_label = length_label
  )
}

pieces <- lapply(seq_along(theta_vals), function(i) {
  make_frame(theta_vals[i], frames[i])
})

lines_df <- do.call(rbind, lapply(pieces, `[[`, "lines"))
middle_df <- do.call(rbind, lapply(pieces, `[[`, "middle"))
mid_inside_df <- do.call(rbind, lapply(pieces, `[[`, "mid_inside"))
theta_df <- do.call(rbind, lapply(pieces, `[[`, "theta_vec"))
intercepts_df <- do.call(rbind, lapply(pieces, `[[`, "intercepts"))
length_df <- do.call(rbind, lapply(pieces, `[[`, "length_label"))

p <- ggplot() +
  geom_hline(yintercept = 0, linewidth = 0.4) +
  geom_vline(xintercept = 0, linewidth = 0.4) +

  geom_line(
    data = circle,
    aes(x, y),
    linewidth = 1.2
  ) +

  geom_line(
    data = lines_df,
    aes(x, y, group = interaction(frame, line)),
    linewidth = 0.8,
    linetype = "dashed",
    color = "gray35"
  ) +

  # Full moving mid-line, very faint
  geom_line(
    data = middle_df,
    aes(x, y, group = frame),
    linewidth = 0.9,
    linetype = "dotted",
    color = "firebrick",
    alpha = 0.18
  ) +

  # Highlighted part of the mid-line inside the semicircle
  geom_segment(
    data = mid_inside_df,
    aes(x = x, y = y, xend = xend, yend = yend),
    linewidth = 1.4,
    linetype = "solid",
    color = "firebrick"
  ) +

  geom_segment(
    data = theta_df,
    aes(x = x, y = y, xend = xend, yend = yend),
    arrow = arrow(length = unit(0.18, "cm")),
    linewidth = 1,
    color = "steelblue"
  ) +

  geom_text(
    data = intercepts_df,
    aes(x, y, label = label),
    parse = TRUE,
    hjust = 0,
    vjust = -0.35,
    size = 4
  ) +

  geom_text(
    data = length_df,
    aes(x, y, label = label),
    hjust = 0,
    size = 4
  ) +

  coord_fixed(xlim = c(-1.25, 1.25), ylim = c(-0.35, 1.3)) +
  labs(
    x = "x",
    y = "y"
  ) +
  theme_minimal(base_size = 13) +

  transition_manual(frame)

animate(
  p,
  fps = fps,
  nframes = n_total,
  width = 900,
  height = 650,
  renderer = gifski_renderer("semicircle_band.gif")
)