Skip to content

Instantly share code, notes, and snippets.

@caudamus
Created October 14, 2018 01:58
Show Gist options
  • Save caudamus/041a4b7ee590b8ae004989a656f1bf53 to your computer and use it in GitHub Desktop.
Save caudamus/041a4b7ee590b8ae004989a656f1bf53 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
from math import atan2, pi, sqrt
'''
Our goal is to figure out an analytic formulation of the area the goat can reach.
*Note:* We'll take advantage of symmetry to only solve the top half of the area.
We have two circles which intersect:
- Goat circle, with radius G, centered at (0, 0)
- Field circle, with radius R, centered at (0, R)
The formulas for these two circles:
- x**2 + y**2 = G**2
- (x - R)**2 + y**2 = R**2 which simplifies to x**2 + y**2 = 2yR
Setting them equal to each other we get:
- x = G**2 / 2R
- y = sqrt(G**2 - x**2)
Now we need to figure out the area that the goat can reach, given this point.
We do this adding taking the sections of both circles,
and then subtracting the overlapping triangle.
The angles of the two circle sections are:
- Goat section, angle alpha = arctan(y, x)
- Field section, angle beta = pi - 2alpha
So the area sections are:
- Goat section area A = alpha G**2 / 2
- Field section area B = beta R**2 / 2
- Overlapping section O = y * R / 2
So we can now calculate the total area (in the top half) reachable by the goat,
- reachable_area = A + B - O
We want to compare to the total area in the top half field
- field_area = pi * R**2 / 2
Diving this reachable area by the field area gives us the reachable fraction
- reachable_fraction = reachable_area / field_area
We'll solve this by binary search for a ratio which gives us reachable_fraction = 1/2.
- Start with the widest possible range: 0 <= ratio <= 2
- Check the fraction reachable
- if it's greater than the target, lower the upper end of the range
- if it's less than the target, raise the lower end of the range
We want to stop at some, point, so we'll stop when the range is < 1e-10.
(If the field is a mile wide, our tether is accurate to ~3 millionths of an inch)
'''
def get_reachable_fraction(ratio):
'''
Input: ratio of goat tether length / field radius
Output: fraction of the field reachable by goat
'''
# NOTE: all of the areas are just considering the top half of the field!
R = 1 # Field radius
G = ratio * R # Goat tether length
x = G**2 / (2 * R) # Intersection point
y = sqrt(G**2 - x**2) # Intersection point
alpha = atan2(y, x) # Goat section angle
beta = pi - 2 * alpha # Field section angle
A = alpha * G**2 / 2 # Goat section area
B = beta * R**2 / 2 # Field section area
overlap = y * R / 2 # Overlapping area
reachable_area = A + B - overlap # This is the top area the goat can reach
field_area = pi * R**2 / 2 # This is the whole (top half) field
return reachable_area / field_area # This is the reachable fraction
# Start with a widest possible range, and binary search!
target_fraction = 0.5 # This is our goal fraction of the field
low_guess, high_guess = 0, 2 # Our initial guesses
allowed_error = 1e-10 # This is how we know we're close enough to stop
# Binary search down to a very narrow range for the correct value
steps = 0
while high_guess - low_guess > allowed_error: # This converges very quickly!
steps += 1
# We'll check the middle of the range and choose the top or bottom half
mid_guess = (high_guess + low_guess) / 2
# If this new guess is too high, bring down the upper bound
if get_reachable_fraction(mid_guess) > target_fraction:
high_guess = mid_guess
else: # Otherwise if it's too low, bring up the lower bound
low_guess = mid_guess
print('Answer: {:.9f}'.format((high_guess + low_guess) / 2)) # 1.158728473
print('Converged after', steps, 'steps') # 35 steps
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment