I want to make a simple process order like :
You filled an order for 1 antacid for a total of $5.33
You filled an order for 3 sour bites for a total of $6.99
My code is:
total = 0
def process_order(x_list):
for x in len(x_list):
print(f"You filled an order for{x[1]} {x[0]} for a total of {x[1]* x[2]}")
x = [("oranges", 4, 3.22),("gummy bears",1,1.99),("sour bites", 3, 2.33), ("antacid", 1, 5.33)]
while(len(x)>0):
process_order(x)
print("Total price: ${:.2f}".format(total))
But I got error
TypeError: 'int' object is not iterable
As far as I understood, this seems like the code you want:
total = 0
def process_order(x_list):
global total
for x in x_list:
print(f"You filled an order for {x[1]} {x[0]} for a total of {x[1]* x[2]}")
total = total + x[1] * x[2]
return
x = [("oranges", 4, 3.22),("gummy bears",1,1.99),("sour bites", 3, 2.33), ("antacid", 1, 5.33)]
process_order(x)
print("Total price: ${:.2f}".format(total))
Output:
You filled an order for 4 oranges for a total of 12.88
You filled an order for 1 gummy bears for a total of 1.99
You filled an order for 3 sour bites for a total of 6.99
You filled an order for 1 antacid for a total of 5.33
Total price: $27.19
You need to define total as a global variable to get a overall total of the prices. Also, you can iterate through the list in python, as for list_element in list. If you want to make a function, it needs return statement at the end. In this case, what you have to do is to add each prices to global variable, and print out the process for each items, there's no need to return something.
You code is can be simplified with the same concepts minus the use of a defined function. First thing to address, is your while len(x) > 0 needs something to decrease the len(x) to end the loop. For this we can use .pop(). This will remove the item at index , we will use 0, and assign that to a variable z. From here we can use your print statement unchanged, I used other formatting for preference. And then we can add the total of each person to our running total.
x = [("oranges", 4, 3.22),("gummy bears",1,1.99),("sour bites", 3, 2.33), ("antacid", 1, 5.33)]
total = 0
while len(x) > 0:
z = x.pop(0)
print('You filled and order for {} {} for a total of {}'.format(z[1], z[0], z[1] * z[2]))
total += z[1] * z[2]
print("Total price: {:.2f}".format(total))
You filled and order for 4 oranges for a total of 12.88
You filled and order for 1 gummy bears for a total of 1.99
You filled and order for 3 sour bites for a total of 6.99
You filled and order for 1 antacid for a total of 5.33
Total price: 27.19
Function name process_order gives an idea, that only one order has been processed. so it's not a good idea to process many orders there at once. Moreso, if you return an order total from this function, you can sum over it afterwards to have total: total = sum(map(process_order, orders)). This will save you a lot of time to improve your functions while line with counting total still be the same.
The sample code below:
def process_order(order):
"""Process an order.
Order is a tuple and contains 3 fields: name, quantity and price.
"""
name, quantity, price = order
total_price = price * quantity
print(f"You filled an order for {]} {} for a total of {.2f}".format(
quantity, name, total_price))
return total_price
orders = [
("oranges", 4, 3.22), ("gummy bears", 1, 1.99),
("sour bites", 3, 2.33), ("antacid", 1, 5.33)]
total = sum(map(process_order, orders)) # can be rewritten with for loop
print("Total price: ${:.2f}".format(total))
Related
tuition = 10966
increase_rate = 0.062
# print headings
print("Year #\t\tProjected Annual Tuition")
print("----------------------------------------")
for y in range(1, 5):
tuition_inc = tuition*increase_rate
tuition += tuition_inc
print(y, "\t\t$", format(tuition, ",.2f"), sep='')
How to sum the column of Projected Annual Tuition? When use the sum function it is summing the years and not the amount of all 4 years tuition combined.
Try first defining the total first outside of the loop -
total = 0
And then inside the loop use
total += tuition
so you can actually save the total, and then have it equal the sum of all the tuition years.
I am trying to do an Optimization problem with PuLP but I am having an issue with writing my objective function.
I've simplified my real-life example to a simpler one using cereal. So let's say I have a list of products and a number of Aisles I can put them into (for this example 2). Each product has a number we normally sell each week (ex: we sell 20 boxes of Fruit Loops and 6 boxes of Cheerios each week). Each item also needs a certain amount of shelves (ex: Frosted Flakes needs 1 shelf, but Corn Flakes needs 2).
Product
Sales
Shelves
Assigned Aisle
Fruit Loops
20
2
Frosted Flakes
15
1
Cocoa Pebbles
8
1
Fruitty Pebbles
9
1
Corn Flakes
12
2
Cheerios
6
1
Each aisle only has 4 shelves. So in theory I could put Fruit Loops and Corn Flakes together in one Aisle (2 shelves + 2 shelves). If I put those in an Aisle the weekly sales from that Aisle would be 20 + 12 = 32. If I put the other 4 items (1 shelf + 1 + 1 + 1) in an Aisle then that Aisles sales would be 15 + 8 + 9 + 6 = 38. The average sales in an Aisle should be 35. The goal of my optimization problem is for each Aisle to be as close to that Average number as possible. I want to minimize the total absolute difference between each Aisles Total Weekly Sales and the Average number. In this example my deviation would be ABS(38-35) + ABS(32-35) = 6. And this is the number I want to minimize.
I cannot figure out the way to write that so PuLP accepts my objective. I can't find an example online with that level of complexity where it's comparing each value to an average and taking the cumulative absolute deviation. And when I write out code that technically would calculate that PuLP doesn't seem to accept it.
Here's some example code:
products = ['Fruit Loops', 'Frosted Flakes', 'Cocoa Pebbles', 'Fruitty Pebbles', 'Corn Flakes', 'Cheerios']
sales = {'Fruit Loops': 20, 'Frosted Flakes': 15, 'Cocoa Pebbles': 8, 'Fruitty Pebbles': 9, 'Corn Flakes': 12, 'Cheerios': 6}
shelves = {'Fruit Loops': 2, 'Frosted Flakes': 1, 'Cocoa Pebbles': 1, 'Fruitty Pebbles': 1, 'Corn Flakes': 2, 'Cheerios': 1}
from pulp import *
problem = LpProblem('AisleOptimization', LpMinimize)
# For this simplified example there are only 2 aisles
Num_of_Aisles = 2
# Each Aisle has 4 shelves and can't have more than 4 shelves filled
Max_Shelves_Aisle = 4
# The Optimizer can change the Aisle each Product is assigned to try and solve the problem
AislePick = LpVariable.dicts('AislePick', products, lowBound = 0, upBound = (Num_of_Aisles - 1), cat='Integer')
# My attempt at the Minimization Formula
# First Calculate what the average sales would be if split perfectly between aisles
avgsales = sum(sales.values()) / Num_of_Aisles
# Loop through and calculate total sales in each aisle and then subtract from the average
problem += lpSum([sum(v for _, v in value) - avgsales for _, value in itertools.groupby(sorted([(aisle, sales[product]) for product, aisle in AislePick.items()]), lambda x: x[0])]), 'Hits Diff'
# Restriction so each Aisle can only have 4 shelves
for aisle in range(Num_of_Aisles):
problem += lpSum([shelves[prod] for prod, ais in AislePick.items() if ais == aisle]) <= Max_Shelves_Aisle, f"Sum_of_Slots_in_Aislel{aisle}"
problem.solve()
The result I get is -3
If I run LpStatus[problem.status] I get Undefined. I assume my is that my objective function is too complex, but I'm not sure.
Any help is appreciated.
Your main issue here is that the ABS function is non-linear. (So is whatever sorting thing you were trying to do... ;) ). So you have to reformulate. The typical way to do this is to introduce a helper variable and consider the "positive" and "negative" deviations separately from the ABS function as both of those are linear. There are several examples of this on the site, including this one that I answered a while back:
How to make integer optimization with absolute values in python?
That introduces the need bring the aisle selection into the index, because you will need to have an index for the aisle sums or diffs. That is not too hard.
Then you have to (as I show below) put in constraints to constrain the new aisle_diff variable to be larger than both the positive or negative deviation from the ABS.
So, I think the below works fine. Note that I introduced a 3rd aisle to make it more interesting/testable. And I left a few comments on your now dead code.
from pulp import *
products = ['Fruit Loops', 'Frosted Flakes', 'Cocoa Pebbles', 'Fruitty Pebbles', 'Corn Flakes', 'Cheerios']
sales = {'Fruit Loops': 20, 'Frosted Flakes': 15, 'Cocoa Pebbles': 8, 'Fruitty Pebbles': 9, 'Corn Flakes': 12, 'Cheerios': 6}
shelves = {'Fruit Loops': 2, 'Frosted Flakes': 1, 'Cocoa Pebbles': 1, 'Fruitty Pebbles': 1, 'Corn Flakes': 2, 'Cheerios': 1}
problem = LpProblem('AisleOptimization', LpMinimize)
# Updated to 3 aisles for testing...
Num_of_Aisles = 3
aisles = range(Num_of_Aisles)
# Each Aisle has 4 shelves and can't have more than 4 shelves filled
Max_Shelves_Aisle = 4
avgsales = sum(sales.values()) / Num_of_Aisles
# The Optimizer can change the Aisle each Product is assigned to try and solve the problem
# value of 1: assign to this aisle
AislePick = LpVariable.dicts('AislePick', indexs=[(p,a) for p in products for a in aisles], cat='Binary')
#print(AislePick)
# variable to hold the abs diff of aisles sales value...
aisle_diff = LpVariable.dicts('aisle_diff', indexs=aisles, cat='Real')
# constraint: Limit aisle-shelf capacity
for aisle in aisles:
problem += lpSum(shelves[product]*AislePick[product, aisle] for product in products) <= Max_Shelves_Aisle
# constraint: All producst must be assigned to exactly 1 aisle (or the model would make no assignments at all...
# or possibly make multiple assignements to balance out)
for product in products:
problem += lpSum(AislePick[product, aisle] for aisle in aisles) == 1
# constraint: the "positive" aisle difference side of the ABS
for aisle in aisles:
problem += aisle_diff[aisle] >= \
lpSum(sales[product]*AislePick[product, aisle] for product in products) - avgsales
# constraint: the "negative" aisle diff...
for aisle in aisles:
problem += aisle_diff[aisle] >= \
avgsales - lpSum(sales[product]*AislePick[product, aisle] for product in products)
# OBJ: minimize the total diff (same as min avg diff)
problem += lpSum(aisle_diff[aisle] for aisle in aisles)
# My attempt at the Minimization Formula
# First Calculate what the average sales would be if split perfectly between aisles
# Loop through and calculate total sales in each aisle and then subtract from the average
# illegal: problem += lpSum([sum(v for _, v in value) - avgsales for _, value in itertools.groupby(sorted([(aisle, sales[product]) for product, aisle in AislePick.items()]), lambda x: x[0])]), 'Hits Diff'
# Restriction so each Aisle can only have 4 shelves
# illegal. You cannot use a conditional "if" statement to test the value of a variable.
# This statement needs to produce a constraint expression independent of the value of the variable...
# for aisle in range(Num_of_Aisles):
# problem += lpSum([shelves[prod] for prod, ais in AislePick.items() if ais == aisle]) <= Max_Shelves_Aisle, f"Sum_of_Slots_in_Aislel{aisle}"
problem.solve()
for (p,a) in [(p,a) for p in products for a in aisles]:
if AislePick[p,a].varValue:
print(f'put the {p} on aisle {a}')
for a in aisles:
aisle_sum = sum(sales[p]*AislePick[p,a].varValue for p in products)
print(f'expectes sales in aisle {a} are ${aisle_sum : 0.2f}')
# for v in problem.variables():
# print(v.name,'=',v.varValue)
Yields:
Result - Optimal solution found
Objective value: 5.33333333
Enumerated nodes: 22
Total iterations: 1489
Time (CPU seconds): 0.10
Time (Wallclock seconds): 0.11
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.10 (Wallclock seconds): 0.11
put the Fruit Loops on aisle 0
put the Frosted Flakes on aisle 2
put the Cocoa Pebbles on aisle 2
put the Fruitty Pebbles on aisle 1
put the Corn Flakes on aisle 1
put the Cheerios on aisle 0
expectes sales in aisle 0 are $ 26.00
expectes sales in aisle 1 are $ 21.00
expectes sales in aisle 2 are $ 23.00
[Finished in 281ms]
I am currently passing the sample tests and 2 of the other 10 cases so 4 out of 12. However, I don't make it through all of the data. I am getting a Terminated due to timeout error which means that my solution isn't fast enough.
def stockmax(prices):
total = 0
for index, price in enumerate(prices):
if index < len(prices) - 1:
section = max(prices[index+1:])
if prices[index] < section:
total += section - prices[index]
return total
I tried to do everything in only one loop. But how exactly can speed this type of question up. I also tried to cut some lines of the code but it is equally as inefficient.
def stockmax(prices):
total = 0
for index, price in enumerate(prices):
if index < len(prices) - 1 and prices[index] < max(prices[index+1:]):
total += max(prices[index+1:]) - prices[index]
return total
Though it passes the same amount of test cases.
I also tried to use heapq but it passes the same test cases and fails due to time.
def stockmax(prices):
total = 0
for index, price in enumerate(prices):
if index < len(prices) - 1:
section = heapq.nlargest(1,prices[index+1:])[0]
if prices[index] < section:
total += section - prices[index]
return total
https://www.hackerrank.com/challenges/stockmax/topics/dynamic-programming-basics
for details on the problem.
https://hr-testcases-us-east-1.s3.amazonaws.com/330/input09.txt?AWSAccessKeyId=AKIAJ4WZFDFQTZRGO3QA&Expires=1538902058&Signature=3%2FnfZzPO8XKRNyGG0Yu9qJIptgk%3D&response-content-type=text%2Fplain
for a link of some test cases but will expire after a while.
Problem
Your algorithms have become so good at predicting the market that you now know what the share price of Wooden Orange Toothpicks Inc. (WOT) will be for the next number of days.
Each day, you can either buy one share of WOT, sell any number of shares of WOT that you own, or not make any transaction at all. What is the maximum profit you can obtain with an optimum trading strategy?
For example, if you know that prices for the next two days are prices = [1,2], you should buy one share day one, and sell it day two for a profit of 1. If they are instead prices = [2,1], no profit can be made so you don't buy or sell stock those days.
Function Description
Complete the stockmax function in the editor below. It must return an integer that represents the maximum profit achievable.
stockmax has the following parameter(s):
prices: an array of integers that represent predicted daily stock prices
Input Format
The first line contains the number of test cases t.
Each of the next t pairs of lines contain:
The first line contains an integer n, the number of predicted prices for WOT.
The next line contains n space-separated integers prices [i], each a predicted stock price for day i.
Constraints
1 <= t <= 10
1 <= n <= 50000
1 <= prices [i] <= 100000
Output Format
Output lines, each containing the maximum profit which can be obtained for the corresponding test case.
Sample Input
3
3
5 3 2
3
1 2 100
4
1 3 1 2
Sample Output
0
197
3
Explanation
For the first case, you cannot obtain any profit because the share price never rises.
For the second case, you can buy one share on the first two days and sell both of them on the third day.
For the third case, you can buy one share on day 1, sell one on day 2, buy one share on day 3, and sell one share on day 4.
Clearly, for any price we can buy, we would want to sell it at the highest price. Fortunately, we are given that highest price. So, iterating backwards, we know the highest future price seen at any point we visit in our travel "back in time."
Python code:
def stockmax(prices):
n = len(prices)
highest = prices[n - 1]
m = [0] * n
# Travel back in time,
# deciding whether to buy or not
for i in xrange(n - 2, -1, -1):
# The most profit buying stock at this point
# is what we may have made the next day
# (which is stored in m[i + 1])
# and what we could make if we bought today
m[i] = m[i + 1] + max(
# buy
highest - prices[i],
# don't buy
0
)
# Update the highest "future price"
highest = max(highest, prices[i])
return m[0]
If you can use Numpy, then something similar to the below should be rather quick (I believe it's the same idea as the answer from #גלעד ברקן).
import numpy as np
with open('.../input09.txt') as fd:
numtests = int(fd.readline().strip())
counter = 0
numvals = 0
vals = None
steps = None
for line in fd:
if (counter % 2 == 0) :
numvals = int(line.strip())
else:
vals = np.fromstring(line, dtype=int, sep=' ', count=numvals)
assert len(vals) == numvals
cum_max = np.maximum.accumulate(vals[::-1])
np.roll(cum_max, -1)
cum_max[len(cum_max) - 1] = 0
delta = (cum_max - vals)
print('#', counter + 1, 'sum:', np.sum(delta * (delta > 0)))
counter += 1
it runs almost instantly on tests from the input09.txt.
Here is my solution written in ruby.
The solution obtained perfect score.
def solution(a)
gain = 0
i = a.count - 1
min = false
mi = false
while i > 0
s = a.delete_at(i)
unless min
mi = a.index(a.min)
min = a[mi]
end
g = s - min
gain = g if g > gain
i -= 1
min = false if i == mi
end
gain
end
I'm trying to modify the python code for Fibonacci mortal rabbits in order to vary the fecundity of the rabbits depending on their age.
Let's make an example.
My rabbits achieve maturity after 3 months and die after 6 months. During their 4 months of fecundity they produce a different number of offspring depending on their age. When they are 3 months old produce 2 pairs of bunnies, at 4 months produce 3 pairs of bunnies and so on till the sixth month. Each pair of bunnies is formed by a female and a male. In the end I would count the number of pairs not the number of individuals.
Fecundity values from the birth to the death:
fecundity = [0, 0, 2, 3, 3, 1]
The python code that I'm using (https://github.com/jschendel/Rosalind/blob/master/011_FIBD.py) is:
n = 12
m = 6
#n = months to run
#m = how many months the rabbits live
# Populate the initial rabbits.
Rabbits = [1]+[0]*(m-1)
# Calculate the new rabbits (bunnies), in a given month.
# Start at use range(1,n) since our initial population is 0 month old.
for month in range(1, n):
Bunnies = 0
# Get the number of Rabbits able to old enough to give birth.
for j in range(1,m):
Bunnies += Rabbits[(month-j-1)%m]
# Bunnies replace the old rabbits who died.
Rabbits[(month)%m] = Bunnies
# Total rabbits is the sum of the living rabbits.
Total_Rabbits = sum(Rabbits)
I'm not sure how to implement the variation of fecundity. Any help is appreciated!
Thank you,
Valentina
Define your fecundity array to stop when a rabbit dies :
fecundity = [0, 0, 2, 3, 3, 1]
means your rabbits die at the age of 7 months old.
After that, you just write a recursive function to calculate the number of new_borns in a specific step. I initialize the steps as 1 pair for step 0, and 0 pair for step < 0. You can of course change it to fit your case. (What I call a step is one unit of time, here the month).
Here is the function :
def new_borns(step):
if step < 0:
return 0
if step == 0:
return 1
nb_newborns = 0
# We create a loop on living pairs
for old_step in range(1, len(fecondity) +1):
nb_newborns += (fecundity[old_step -1]) * new_borns(step - old_step)
return nb_newborns
The total population of a specific step is the total of new_borns for the previous steps, still living (ie, for the length of your fecundity array).
def FIBD(step):
population = 0
for i in range(len(fecundity)):
population += new_borns(step - i)
return population
To know how many pairs you have at step 7, just call FIBD(7) The number of months the rabbit can live is the length of the fecundity array.
Of course, this recursive function is very very slow and bad. You need a caching system to avoid to calculate multiple times the same step. Here is the full file to write.
#!/usr/bin/env python
fecundity = [0, 0, 2, 3, 3, 1]
new_borns_cache = [1]
def new_borns(step):
if step < 0:
return 0
try :
return new_borns_cache[step]
except IndexError:
if step == 0:
return 1
sum = 0
for old_step in range(1, len(fecundity) +1):
sum += (fecundity[old_step -1]) * new_borns(step - old_step)
return sum
def fibd(step):
population = 0
for i in range(len(fecundity)):
population += new_borns(step - i)
return population
To use it, just import , and call fibd(7)
I came to an answer myself and I really modified the code that I posted previously. I think that now it is much simpler
import numpy as np
m = 15
n = 18
fecundity = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 2, 1, 1, 1])
Bunnies = np.array([0]*m)
Rabbits = np.array([1]+[0]*(m-1))
for month in range(0, 18):
# every month I shift the list of 1 since they're getting older
Rabbits = np.roll(Rabbits, 1)
# I set the newborns as 0
Rabbits[0] = 0
# I calculate the newborns
Bunnies = Rabbits * fecundity
# and then I assign them to the rabbits 0 month old
Rabbits[0] = sum(Bunnies)
# the sum of Rabbits is the number of final pairs of Rabbits after n months
Total_Rabbits = sum(Rabbits)
# 26 Rabbits
Could I code differently to slim down the point of this Python source code? The point of the program is too get the users total amount and add it too the shipping cost. The shipping cost is determined by both country (Canada or USA) and price of product:
The shipping of a product that is $125.00 in Canada is $12.00.
input ('Please press "Enter" to begin')
while True:
print('This will calculate shipping cost and your grand total.')
totalAmount = int(float(input('Enter your total amount: ').replace(',', '').replace('$', '')))
Country = str(input('Type "Canada" for Canada and "USA" for USA: '))
usa = "USA"
canada = "Canada"
lessFifty = totalAmount <= 50
fiftyHundred = totalAmount >= 50.01 and totalAmount <= 100
hundredFifty = totalAmount >= 100.01 and totalAmount <= 150
twoHundred = totalAmount
if Country == "USA":
if lessFifty:
print('Your shipping is: $6.00')
print('Your grand total is: $',totalAmount + 6)
elif fiftyHundred:
print('Your shipping is: $8.00')
print('Your grand total is: $',totalAmount + 8)
elif hundredFifty:
print('Your shipping is: $10.00')
print('Your grand total is: $',totalAmount + 10)
elif twoHundred:
print('Your shipping is free!')
print('Your grand total is: $',totalAmount)
if Country == "Canada":
if lessFifty:
print('Your shipping is: $8.00')
print('Your grand total is: $',totalAmount + 8)
elif fiftyHundred:
print('Your shipping is: $10.00')
print('Your grand total is: $',totalAmount + 10)
elif hundredFifty:
print('Your shipping is: $12.00')
print('Your grand total is: $',totalAmount + 12)
elif twoHundred:
print('Your shipping is free!')
print('Your grand total is: $',totalAmount)
endProgram = input ('Do you want to restart the program?')
if endProgram in ('no', 'No', 'NO', 'false', 'False', 'FALSE'):
break
Here is the core to simplify your code. It prints shipping cost for $100.00 in USA
totalAmount = 100
chargeCode = (int(100*(totalAmount+0.001))-1)/5000 #0 -- <=50, 1 -- 50.01-100, etc
if chargeCode > 3: chargeCode = 3
shipping = {}
shipping[("USA", 0)] = 6
shipping[("USA", 1)] = 8
shipping[("USA", 2)] = 10
shipping[("USA", 3)] = 0
shipping[("Canada", 0)] = 8
shipping[("Canada", 1)] = 10
shipping[("Canada", 2)] = 12
shipping[("Canada", 3)] = 0
print shipping[("USA", chargeCode)]
totalAmount+0.001 is used to avoid fun with float point numbers:
int(100*(81.85)) == 8184
returns True on my system because float point 81.85 is a bit less than decimal 81.85.
Here's a basic strategy for the cost calculation:
import math
amount = int(totalAmount)
assert amount >= 0
shipping = {
'USA': [6, 8, 10],
'Canada': [8, 10, 12]
}
try:
surcharge = shipping[country][amount and (math.ceil(amount / 50.0) - 1)]
except IndexError:
surcharge = 0
total = amount + surcharge
The key notion here is that the the shipping cost ranges follow a fairly linear progression: [0-50], (50-100], (100-150], (150, inf)
Note that the first group is a little funny, as it includes the lower bound of 0 where the other groups don't include the lower bound (they are an open interval at the bottom). So going forward we'll consider the first group as the following: 0 or (0-50]
We want to transform the amount the user supplies into an index into the shipping cost lists [6, 8, 10] and [8, 10, 12]. The lists are of length 3, so the indexes are 0, 1 and 2. Notice that if we divide any number in the range (0, 150] by 50.0 (we add the .0 to 50 so we a get a real number back -- 1 / 50 == 0 but 1 / 50.0 == 0.02 -- for the next step) we get a number in the range (0 and 3].
Now realize that math.ceil will round a real number to the nearest whole number that is greater than or equal to itself -- ex. math.ceil(.001) == 1.0, math.ceil(1.5) == 2.0, math.ceil(2) == 2.0. So applying math.ceil to numbers in the range (0, 3] we will supply either 1.0, 2.0 or 3.0. Cast those numbers to int (int(2.0) == 2) and we get the values 1, 2 and 3. Subtract 1 from those values and we get the 0, 1, 2. Voila! Those numbers match the indexes into our shipping array.
You can express this transformation with the Python: int(math.ceil(amount / 50.0) - 1)
We are almost there. We've handled any amount in the range (0, 150]. But what if amount is 0. math.ceil(0) == 0.0 and 0.0 - 1 == -1.0 This will not index properly into our array. So we handle 0 separately by checking first if amount is equal to 0, and if it is, using 0 as our shipping array index instead of the applying our transformation to amount to determine the index. This can be accomplished using Python's short circuit and operator (the web should have plenty of information on this) in the following expression: amount and int(math.ceil(amount / 50.0) - 1)
Note that any value above 150 will reduce to an index greater than 2 and applying that index against the shipping array will result in an IndexError. But any value greater than 150 has free shipping so we can catch the IndexError and apply a surcharge of 0:
try:
surcharge = shipping[country][amount and int(math.ceil(amount / 50.0) - 1)]
except IndexError:
surcharge = 0
And we're done!