import math
def total_horizontal_displacement(alpha0, speeds, widths, boundary_normal_angle):
"""
Given an initial angle alpha0 (radians, measured relative to the layer normal),
propagate the refraction (using Snell’s law) through the layers and compute
the total horizontal displacement.
The layers have a width (measured along the normal) given by widths[i].
The absolute travel direction in layer i is (boundary_normal_angle - alpha[i]).
"""
n = len(speeds)
alpha = [0.0] * n
alpha[0] = alpha0
# Propagate using Snell’s law: v_i * sin(alpha_i) = v_(i+1) * sin(alpha_(i+1))
for i in range(n - 1):
ratio = (speeds[i] / speeds[i+1]) * math.sin(alpha[i])
if abs(ratio) > 1:
return 1e9 # No real solution in this branch.
alpha[i+1] = math.asin(ratio)
horiz = 0.0
for i in range(n):
# Distance traveled in layer i
L = widths[i] / math.cos(alpha[i])
# Absolute travel angle relative to horizontal:
theta = boundary_normal_angle - alpha[i]
horiz += L * math.cos(theta)
return horiz
def total_time(alpha0, speeds, widths, boundary_normal_angle):
"""
Compute total travel time given the initial angle alpha0.
"""
n = len(speeds)
alpha = [0.0] * n
alpha[0] = alpha0
for i in range(n - 1):
ratio = (speeds[i] / speeds[i+1]) * math.sin(alpha[i])
if abs(ratio) > 1:
return 1e9
alpha[i+1] = math.asin(ratio)
time = 0.0
for i in range(n):
L = widths[i] / math.cos(alpha[i])
time += L / speeds[i]
return time
def solve_snell(speeds, widths, boundary_normal_angle, target):
"""
Uses binary search on alpha0 so that the total horizontal displacement equals the target.
Returns the optimal alpha0 (in radians) and the corresponding total travel time.
"""
def f(alpha0):
return total_horizontal_displacement(alpha0, speeds, widths, boundary_normal_angle) - target
low = 0.0
high = math.radians(10) # Start with a small guess.
# Increase high until f(high) > 0 (i.e. horizontal displacement exceeds target)
while f(high) < 0:
high *= 2
if high > math.radians(89): # safeguard
break
# Binary search for alpha0
for _ in range(100):
mid = (low + high) / 2.0
if f(mid) > 0:
high = mid
else:
low = mid
alpha_opt = (low + high) / 2.0
t_opt = total_time(alpha_opt, speeds, widths, boundary_normal_angle)
return alpha_opt, t_opt
def main():
# Speeds in each layer:
# Layers 0 and 6 represent terrain outside the marsh (10 leagues/day),
# layers 1-5 are inside the marsh.
speeds = [10, 9, 8, 7, 6, 5, 10]
# Each layer has width 10 leagues measured along the normal to the boundaries.
widths = [10] * 7
# The marsh boundaries are oriented at 45° relative to horizontal.
# Thus the boundary normal is at 45°.
boundary_normal_angle = math.radians(45)
# We require that the total horizontal displacement equals 100 leagues.
target = 100.0
alpha_opt, t_opt = solve_snell(speeds, widths, boundary_normal_angle, target)
print("Optimal initial angle (relative to normal) = {:.6f} degrees".format(math.degrees(alpha_opt)))
print("Total travel time = {:.13f} days".format(t_opt))
if __name__ == "__main__":
main()