import math
# --- problem setup ---
A = (0.0, 0.0)
B = (100.0, 0.0)
L = [25.0, 15.0, 5.0, -5.0, -15.0, -25.0] # marsh‑boundary offsets
speeds = [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 10.0] # segment speeds
def total_time(x_vec):
# build crossing points
pts = [A]
for xi, Li in zip(x_vec, L):
yi = xi - 50.0 + Li * math.sqrt(2)
pts.append((xi, yi))
pts.append(B)
# sum segment times
t = 0.0
for i in range(len(speeds)):
x0, y0 = pts[i]
x1, y1 = pts[i+1]
d = math.hypot(x1 - x0, y1 - y0)
t += d / speeds[i]
return t
# initial guess: direct route intersections
x = [50.0 - Li * math.sqrt(2) for Li in L]
# gradient descent parameters
alpha = 1.0
eps = 1e-6
tol = 1e-8
max_iters = 10000
def numerical_grad(f, x):
grad = [0.0]*len(x)
for i in range(len(x)):
x_eps_p = x.copy(); x_eps_p[i] += eps
x_eps_m = x.copy(); x_eps_m[i] -= eps
grad[i] = (f(x_eps_p) - f(x_eps_m)) / (2*eps)
return grad
for _ in range(max_iters):
g = numerical_grad(total_time, x)
# compute new candidate
x_new = [xi - alpha*gi for xi, gi in zip(x, g)]
if total_time(x_new) < total_time(x):
x = x_new
else:
alpha *= 0.5
if math.sqrt(sum(gi*gi for gi in g)) < tol:
break
opt_time = total_time(x)
print(f"Optimal time: {opt_time:.10f} days")
aW1wb3J0IG1hdGgKCiMgLS0tIHByb2JsZW0gc2V0dXAgLS0tCkEgPSAoMC4wLCAwLjApCkIgPSAoMTAwLjAsIDAuMCkKTCA9IFsyNS4wLCAxNS4wLCA1LjAsIC01LjAsIC0xNS4wLCAtMjUuMF0gICAgICAgIyBtYXJzaOKAkWJvdW5kYXJ5IG9mZnNldHMKc3BlZWRzID0gWzEwLjAsIDkuMCwgOC4wLCA3LjAsIDYuMCwgNS4wLCAxMC4wXSAgICMgc2VnbWVudCBzcGVlZHMKCmRlZiB0b3RhbF90aW1lKHhfdmVjKToKICAgICMgYnVpbGQgY3Jvc3NpbmcgcG9pbnRzCiAgICBwdHMgPSBbQV0KICAgIGZvciB4aSwgTGkgaW4gemlwKHhfdmVjLCBMKToKICAgICAgICB5aSA9IHhpIC0gNTAuMCArIExpICogbWF0aC5zcXJ0KDIpCiAgICAgICAgcHRzLmFwcGVuZCgoeGksIHlpKSkKICAgIHB0cy5hcHBlbmQoQikKICAgICMgc3VtIHNlZ21lbnQgdGltZXMKICAgIHQgPSAwLjAKICAgIGZvciBpIGluIHJhbmdlKGxlbihzcGVlZHMpKToKICAgICAgICB4MCwgeTAgPSBwdHNbaV0KICAgICAgICB4MSwgeTEgPSBwdHNbaSsxXQogICAgICAgIGQgPSBtYXRoLmh5cG90KHgxIC0geDAsIHkxIC0geTApCiAgICAgICAgdCArPSBkIC8gc3BlZWRzW2ldCiAgICByZXR1cm4gdAoKIyBpbml0aWFsIGd1ZXNzOiBkaXJlY3Qgcm91dGUgaW50ZXJzZWN0aW9ucwp4ID0gWzUwLjAgLSBMaSAqIG1hdGguc3FydCgyKSBmb3IgTGkgaW4gTF0KCiMgZ3JhZGllbnQgZGVzY2VudCBwYXJhbWV0ZXJzCmFscGhhID0gMS4wCmVwcyA9IDFlLTYKdG9sID0gMWUtOAptYXhfaXRlcnMgPSAxMDAwMAoKZGVmIG51bWVyaWNhbF9ncmFkKGYsIHgpOgogICAgZ3JhZCA9IFswLjBdKmxlbih4KQogICAgZm9yIGkgaW4gcmFuZ2UobGVuKHgpKToKICAgICAgICB4X2Vwc19wID0geC5jb3B5KCk7IHhfZXBzX3BbaV0gKz0gZXBzCiAgICAgICAgeF9lcHNfbSA9IHguY29weSgpOyB4X2Vwc19tW2ldIC09IGVwcwogICAgICAgIGdyYWRbaV0gPSAoZih4X2Vwc19wKSAtIGYoeF9lcHNfbSkpIC8gKDIqZXBzKQogICAgcmV0dXJuIGdyYWQKCmZvciBfIGluIHJhbmdlKG1heF9pdGVycyk6CiAgICBnID0gbnVtZXJpY2FsX2dyYWQodG90YWxfdGltZSwgeCkKICAgICMgY29tcHV0ZSBuZXcgY2FuZGlkYXRlCiAgICB4X25ldyA9IFt4aSAtIGFscGhhKmdpIGZvciB4aSwgZ2kgaW4gemlwKHgsIGcpXQogICAgaWYgdG90YWxfdGltZSh4X25ldykgPCB0b3RhbF90aW1lKHgpOgogICAgICAgIHggPSB4X25ldwogICAgZWxzZToKICAgICAgICBhbHBoYSAqPSAwLjUKICAgIGlmIG1hdGguc3FydChzdW0oZ2kqZ2kgZm9yIGdpIGluIGcpKSA8IHRvbDoKICAgICAgICBicmVhawoKb3B0X3RpbWUgPSB0b3RhbF90aW1lKHgpCnByaW50KGYiT3B0aW1hbCB0aW1lOiB7b3B0X3RpbWU6LjEwZn0gZGF5cyIp