Skip to content

Commit 34d033a

Browse files
committed
Adding 2 non OO approaches to solving this. Hopefully verbose enough to be self
documenting. The second one even gets the corect answer...
1 parent af7381b commit 34d033a

File tree

7 files changed

+410
-0
lines changed

7 files changed

+410
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python
2+
3+
from method_a import calc_max_bananas_transported as calc_max_bananas_transported_a
4+
from method_b import calc_max_bananas_transported as calc_max_bananas_transported_b
5+
6+
GLOBAL_BANANA_COUNT = 3000
7+
MAX_BANANA_CARRY = 1000
8+
TOTAL_DISTANCE_TO_TRAVEL = 1000
9+
10+
def main():
11+
12+
print("Calling method A :")
13+
bananas_left_A = calc_max_bananas_transported_a( GLOBAL_BANANA_COUNT,
14+
MAX_BANANA_CARRY,
15+
TOTAL_DISTANCE_TO_TRAVEL)
16+
17+
print("", "="*30, "\n")
18+
print("Calling method B :")
19+
bananas_left_B = calc_max_bananas_transported_b( GLOBAL_BANANA_COUNT,
20+
MAX_BANANA_CARRY,
21+
TOTAL_DISTANCE_TO_TRAVEL)
22+
print("\n", "="*30, "\n")
23+
print(f"method A reckons I can shift {bananas_left_A}")
24+
print(f"method B reckons I can shift {bananas_left_B}")
25+
print("", "="*30, "\n")
26+
27+
if __name__ == '__main__':
28+
main()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env python
2+
3+
def bananas_transported(initial_banana_load, distance, make_return_trip=True):
4+
'''Calculated the number of bananas transprted onwards allowing for both
5+
single and return trip costs'''
6+
bananas_transported = initial_banana_load - distance
7+
if make_return_trip:
8+
bananas_transported -= distance
9+
if bananas_transported < 0 :
10+
raise ValueError('Distance too far, the camel has died....')
11+
return bananas_transported
12+
13+
def calc_bananas_transported(initial_banana_count, distance, camels_carry_limit):
14+
'''Calculates the total number of bananas moved along one leg of the total
15+
journey'''
16+
bananas_left = initial_banana_count
17+
transported_bananas = 0
18+
# Iterate over return trips to shift bananas while it's worth doing so :
19+
# i.e. there are more bananas left than it takes to make a final trip
20+
# after a 'full load' has been transported
21+
while (bananas_left > camels_carry_limit + distance):
22+
carried_bananas = min(bananas_left, camels_carry_limit)
23+
transported_bananas += bananas_transported(carried_bananas, distance,
24+
True)
25+
bananas_left -= carried_bananas
26+
# Make the final one-way trip to next banana_store carrying as many bananas
27+
# as possible.
28+
carried_bananas = min(bananas_left, camels_carry_limit)
29+
transported_bananas += bananas_transported(carried_bananas, distance,
30+
False)
31+
return transported_bananas
32+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python
2+
3+
from common_functions import bananas_transported, calc_bananas_transported
4+
5+
def calc_furthest_limit(bananas_to_shift, camels_carry_limit,
6+
distance_left_to_travel, return_trip=True):
7+
'''Theoretically, How far can a camel go at 1 banana/km...
8+
Assuming that if there are more bananas than the camel can carry, it needs
9+
to make a return trip and can olny go half as far as the number of bananas
10+
it can carry. If no return trip, range is limited by the number of banans.'''
11+
limit_by_banana = min(bananas_to_shift, camels_carry_limit)
12+
# Check if a return trip needs to be made.
13+
# I appreciate this is /wrong/ in the case of abandoning bananas not worth
14+
# returning for - but that should be handled elsewhere.
15+
if (bananas_to_shift > camels_carry_limit):
16+
limit_by_banana = limit_by_banana // 2
17+
furthest_possible = min(distance_left_to_travel, limit_by_banana)
18+
return furthest_possible
19+
20+
def calc_next_stop(bananas_to_shift, camels_carry_limit,
21+
distance_left_to_travel):
22+
'''The cost function : asumes that the highest score of number of bananas
23+
transported * distance travelled is the optimum distance to go before making
24+
a 'banana store'
25+
This turns out to be the wrong approach.
26+
2nd attempt was to try and find the minimum cost in terms of bananas eaten
27+
per km travelled - it failed also.'''
28+
max_banana_miles = 0
29+
best_distance = -100
30+
best_bananas_transported = -100
31+
min_banana_cost = bananas_to_shift
32+
best_distance_2 = -100
33+
best_bananas_transported_2 = -100
34+
furthest_limit = calc_furthest_limit(bananas_to_shift,
35+
camels_carry_limit,
36+
distance_left_to_travel)
37+
for propsed_distance in range(1,furthest_limit + 1):
38+
bananas_transported = calc_bananas_transported(bananas_to_shift,
39+
propsed_distance, camels_carry_limit)
40+
banana_miles = bananas_transported * propsed_distance
41+
if banana_miles > max_banana_miles:
42+
max_banana_miles = banana_miles
43+
best_distance = propsed_distance
44+
best_bananas_transported = bananas_transported
45+
#print("New high score of {0} moving {1:5d} {2:5d} kilometers".format(
46+
# max_banana_miles, bananas_transported, propsed_distance))
47+
#elif banana_miles == max_banana_miles:
48+
# print("Curious_point = got a double identical score of "
49+
# "{0:5d} at {1:5d} and {2:5d}".format(max_banana_miles,
50+
# best_distance, propsed_distance))
51+
# Second attempt at cost function - even worse than the the first....
52+
# Then I realised why - you need the cost of the next leg, not the current one
53+
# to decide where to place the banana store.... I'd be very curious if there was
54+
# a cost function which would iterate from 1 to n km and calculate a
55+
# trasportation cost that gave the cutoff for the first leg at the same place as
56+
# one which works out when the cost of the next leg changes and then calculates
57+
# how far you can go until that happens...
58+
# milage_cost = (bananas_to_shift - bananas_transported) / propsed_distance
59+
# bananana_cost = bananas_transported / milage_cost
60+
# if bananana_cost < min_banana_cost:
61+
# min_banana_cost = bananana_cost
62+
# best_distance = propsed_distance
63+
# best_bananas_transported = bananas_transported
64+
# #print("New best cost of {0} moving {1:5d} {2:5d} kilometers".format(
65+
# # min_banana_cost, bananas_transported, propsed_distance))
66+
# #elif milage_cost == min_banana_cost:
67+
# # print("Curious_point = got a double identical cost of "
68+
# # "{0} at {1:5d} and {2:5d}".format(min_banana_cost,
69+
# # best_distance, propsed_distance))
70+
return best_distance, best_bananas_transported
71+
72+
73+
def calc_max_bananas_transported(initial_banana_count, camels_carry_limit,
74+
distance_to_transport):
75+
'''Given a total distance to cover, and an intial number of bananas along
76+
with a load limit for the camel - calculates the maximum number of bananas
77+
that can be delivered to the destination.'''
78+
distance_left_to_go = distance_to_transport
79+
bananas_left = initial_banana_count
80+
while distance_left_to_go > 0:
81+
(best_leg, bananas_left) = calc_next_stop(bananas_left,
82+
camels_carry_limit, distance_left_to_go)
83+
distance_left_to_go = distance_left_to_go - best_leg
84+
print(f"Have travelled {best_leg}, got {bananas_left} bananas left "
85+
f"and {distance_left_to_go} kilometers to go")
86+
return bananas_left
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python
2+
3+
from common_functions import bananas_transported, calc_bananas_transported
4+
5+
def ceil_div(a, b):
6+
''' Ceiling division - rounds up to the next whole integer'''
7+
return -(-a // b)
8+
9+
def calc_next_stop(bananas_to_shift, camels_carry_limit,
10+
distance_left_to_travel):
11+
''' Works out distance until the number of bananas remaining is one less
12+
load than is currently available. Which is where the 'cost' of onward travel
13+
drops'''
14+
bananas_avail = bananas_to_eat(bananas_to_shift, camels_carry_limit)
15+
distance = how_far_on_a_banana(bananas_to_shift, camels_carry_limit,
16+
bananas_avail, distance_left_to_travel)
17+
return distance
18+
19+
def bananas_to_eat(bananas_to_shift, camels_carry_limit):
20+
''' Works out the numbr of bananas that can be consumed before the number of
21+
loads that can be carried drops.'''
22+
n_loads_at_next_store = (bananas_to_shift -1) // camels_carry_limit
23+
return bananas_to_shift - (n_loads_at_next_store * camels_carry_limit)
24+
25+
def how_far_on_a_banana(bananas_to_shift, camels_carry_limit, bananas_avail,
26+
distance_left_to_travel):
27+
'''Given an arbitrary number of bananas to 'consume' and the number of loads
28+
to shift onwards - calculates the maximum distance that can be travelled'''
29+
no_of_loads = ceil_div(bananas_to_shift, camels_carry_limit)
30+
no_of_trips = (no_of_loads * 2) - 1
31+
distance = min(bananas_avail // no_of_trips, distance_left_to_travel)
32+
if distance == 0 and bananas_to_shift > camels_carry_limit:
33+
print("Using emergency banana munch protocol...")
34+
no_of_trips -= 2
35+
bananas_avail = camels_carry_limit
36+
distance = min(bananas_avail // no_of_trips, distance_left_to_travel)
37+
return distance
38+
39+
def calc_max_bananas_transported(initial_banana_count, camels_carry_limit,
40+
distance_to_transport):
41+
'''Given a total distance to cover, and an intial number of bananas along
42+
with a load limit for the camel - calculates the maximum number of bananas
43+
that can be delivered to the destination.'''
44+
distance_left_to_go = distance_to_transport
45+
bananas_left = initial_banana_count
46+
while distance_left_to_go > 0:
47+
best_leg = calc_next_stop(bananas_left,
48+
camels_carry_limit, distance_left_to_go)
49+
bananas_left = calc_bananas_transported(bananas_left, best_leg,
50+
camels_carry_limit)
51+
distance_left_to_go = distance_left_to_go - best_leg
52+
print(f"Have travelled {best_leg}, got {bananas_left} bananas left "
53+
f"and {distance_left_to_go} kilometers to go")
54+
return bananas_left
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import pytest
2+
from common_functions import bananas_transported, calc_bananas_transported
3+
4+
GLOBAL_BANANA_COUNT = 3000
5+
MAX_BANANA_CARRY = 1000
6+
TOTAL_DISTANCE_TO_TRAVEL = 1000
7+
8+
TEST_LEG_DISTANCE = 200
9+
10+
def test_bananas_transported_return_trip():
11+
return_trip = True
12+
no_bananas_transported = bananas_transported(MAX_BANANA_CARRY,
13+
TEST_LEG_DISTANCE,
14+
return_trip)
15+
assert no_bananas_transported == MAX_BANANA_CARRY - ( 2 * TEST_LEG_DISTANCE)
16+
17+
def test_bananas_transported_one_way_trip():
18+
return_trip = False
19+
no_bananas_transported = bananas_transported(MAX_BANANA_CARRY,
20+
TEST_LEG_DISTANCE,
21+
return_trip)
22+
assert no_bananas_transported == MAX_BANANA_CARRY - TEST_LEG_DISTANCE
23+
24+
def test_bananas_transported_too_far():
25+
intial_load = TEST_LEG_DISTANCE // 2
26+
return_trip = False
27+
with pytest.raises(ValueError):
28+
no_bananas_transported = bananas_transported(intial_load,
29+
TEST_LEG_DISTANCE,
30+
return_trip)
31+
32+
def test_calc_bananas_transported_3000_200km():
33+
no_bananas_transported = calc_bananas_transported(GLOBAL_BANANA_COUNT,
34+
TEST_LEG_DISTANCE,
35+
MAX_BANANA_CARRY)
36+
assert no_bananas_transported == 2000
37+
38+
def test_calc_bananas_transported_2600_200km():
39+
no_bananas_transported = calc_bananas_transported(2600,
40+
TEST_LEG_DISTANCE,
41+
MAX_BANANA_CARRY)
42+
assert no_bananas_transported == 1600
43+
44+
def test_calc_bananas_transported_too_far():
45+
with pytest.raises(ValueError):
46+
no_bananas_transported = calc_bananas_transported(2600,
47+
600,
48+
MAX_BANANA_CARRY)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import pytest
2+
from method_a import calc_furthest_limit, calc_next_stop, calc_max_bananas_transported
3+
4+
GLOBAL_BANANA_COUNT = 3000
5+
MAX_BANANA_CARRY = 1200
6+
TOTAL_DISTANCE_TO_TRAVEL = 1000
7+
8+
TEST_LEG_DISTANCE = 200
9+
10+
def test_calc_furthest_limit_sub_full_load():
11+
'''If the number of bananas you've got is less than a full load, you don't
12+
need to come back for a second load and the furtest you could go is 1km for
13+
every banana you have.'''
14+
bananas_to_shift = max(MAX_BANANA_CARRY-100, 1)
15+
max_theoretical_distance = bananas_to_shift
16+
furthest_limit = calc_furthest_limit(bananas_to_shift, MAX_BANANA_CARRY,
17+
max_theoretical_distance)
18+
assert furthest_limit == max_theoretical_distance
19+
20+
def test_calc_furthest_limit_exceed_full_load():
21+
'''If the number of bananas you have is greater than a full load, you need
22+
to come back for the others and the furthest you can go is 1k for every 2
23+
babnanas you can carry.
24+
: I realise this is actually wrong - there is a case where it is more
25+
efficient to abandon some bananas and not come back. Initially the 'cost
26+
function' was supposed to highlight that distance and this was just the
27+
furthest theoretical - it's still wrong because that approach is flawed :
28+
Discuss.....'''
29+
bananas_to_shift = MAX_BANANA_CARRY+100
30+
furthest_limit = calc_furthest_limit(bananas_to_shift, MAX_BANANA_CARRY,
31+
TOTAL_DISTANCE_TO_TRAVEL)
32+
assert furthest_limit == MAX_BANANA_CARRY // 2
33+
34+
def test_calc_furthest_limit_last_leg():
35+
'''If you've got more bananas than the distance to the final goal, but less
36+
than a full load so no return trip will be required, the furthest you need
37+
to go is the remaining distance.'''
38+
bananas_to_shift = max(MAX_BANANA_CARRY-1, 1)
39+
distance_remaining = max(bananas_to_shift-1 , 1)
40+
furthest_limit = calc_furthest_limit(bananas_to_shift, MAX_BANANA_CARRY,
41+
distance_remaining)
42+
assert furthest_limit == distance_remaining
43+
44+
def test_calc_next_stop_first_leg():
45+
'''This is where writing tests post writing code is interesting:
46+
A : I didn't know before writing the code what the answer to this function
47+
would be - so how do I write a test?.
48+
B : After writing the code, and ruuning it I spotted that this function will
49+
give the wrong answer and I'm not sure it can be fixed as the methodology is
50+
flawed.
51+
This test just fails if the answer changes from the first time it was run.'''
52+
distance, bananas_left = calc_next_stop(GLOBAL_BANANA_COUNT,
53+
MAX_BANANA_CARRY,
54+
TOTAL_DISTANCE_TO_TRAVEL)
55+
assert distance == 300
56+
assert bananas_left == 1500
57+
58+
def test_calc_next_stop_last_leg():
59+
'''This is where writing tests post writing code is interesting:
60+
See previous test for reasons.
61+
This test just fails if the answer changes from the first time it was run.'''
62+
last_leg = max(MAX_BANANA_CARRY // 2, 1)
63+
distance, bananas_left = calc_next_stop(MAX_BANANA_CARRY,
64+
MAX_BANANA_CARRY,
65+
last_leg)
66+
assert distance == last_leg
67+
assert bananas_left == MAX_BANANA_CARRY - last_leg
68+
69+
calc_max_bananas_transported
70+
def test_calc_max_bananas_transported():
71+
'''Another test where I didn't know the answer before running the code.
72+
So Again, this just tests if the answer has changed from the previous run
73+
where a human decidied the answer would do.'''
74+
bananas_left = calc_max_bananas_transported(GLOBAL_BANANA_COUNT,
75+
MAX_BANANA_CARRY,
76+
TOTAL_DISTANCE_TO_TRAVEL)
77+
assert bananas_left == 500
78+

0 commit comments

Comments
 (0)