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]
Related
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))
My task was to
'Write a function selectCoins that asks the user to enter an amount of money
(in pence) and then outputs the number of coins of each denomination (from £2 down
to 1p) that should be used to make up that amount exactly (using the least possible
number of coins). For example, if the input is 292, then the function should report:
1 × £2, 0 × £1, 1 × 50p, 2 × 20p, 0 × 10p, 0 × 5p, 1 × 2p, 0 × 1p. (Hint: use integer
division and remainder).'
def selectCoins():
twopound = 200
onepound = 100
fiftyp = 50
twentyp = 20
tenp = 10
fivep = 5
twop = 2
onep = 1
a = 0
b = 0
c = 0
d = 0
e = 0
f = 0
g = 0
h = 0
money = int(input('Enter how much money you have in pence'))
while True:
if money >= twopound:
money = money - twopound
a = a + 1
elif money >= onepound:
money = money - onepound
b = b + 1
elif money >= fiftyp:
money = money - fiftyp
c = c + 1
elif money >= twentyp:
money = money - twentyp
d = d + 1
elif money >= tenp:
money = money - tenp
e = e + 1
elif money >= fivep:
money = money - fivep
f = f + 1
elif money >= twop:
money = money - twop
g = g + 1
elif money >= onep:
money = money - onep
h = h + 1
else:
money = 0
break
print(a,b,c,d,e,f,g,h)
I am new to programming so when I run this code it just inputs
'1 0 0 0 0 0 0 0' when I type 292 instead of what it should output.
Since you're new to coding, you should start writing the procedure you'd follow on paper, and then find out which tools you can use to automate this process.
Important
Read the full answer in order!
Don't fall for the temptation of reading the code right away.
The solutions I provide are hidden, but you can read them hovering your mouse over them or clicking on them (if you're using StackExchange mobile app, touch the "spoiler" link in each block).
The algorithm
What I'd do is:
Assume I have bins with coins, each bin labeled with the coin denomination.
The bins are sorted from the largest to the lowest denomination, and I always pick as much coins as I need from the highest denomination bin before moving to the next bin.
Write in a piece of paper the value for which I need to calculate the number of coins of each denomination I need.
Start with the first bin (the one holding the highest denomination).
Pick as many coins I need from that bin, in such a way that I don't "overshoot" the amount written on the piece of paper (notice that this number can be zero).
This can be done with an integer division; for example, if your value is 700 and the bin has denomination 200, you calculate the integer division 700 ÷ 200 = 3 (plus a remainder of 100)
Calculate the total amount of the coins I've picked.
Strike the value calculated in step 5 and write the remainder as a "new" value.
Since you've already calculated the integer division in step 4, you can calculate the remainder. You can also consider that there's a "Modulo" operator in most programming languages that will give you the remainder of an integer division right away. Using the above example, 700 mod 200 = 100, which reads "700 modulo 200 is 100", or "The remainder of the integer division 700 ÷ 200 is 100".
Move on to the next bin of coins.
Repeat from step 4 until I use all the bins or the value is zero.
Example
Suppose I start with a value of 292 and I have bins with the following denominations (already sorted from highest to lowest denominations):
| 200 | 100 | 50 | 20 | 10 | 5 | 2 | 1 |
+------+------+------+------+------+------+------+------+
| I | II | III | IV | V | VI | VII | VIII |
So, let's see what happens if I apply the algorithm above:
Write the value: 292
Start with the first bin (denomination: 200)
Pick 1 coin from the bin
The total amount picked from the bin is 200
The remainder is 92
Strike the previous value
The new value is 92
Move to the next bin (denomination: 100)
Pick 0 coins from the bin
The total amount picked from the bin is 0
The remainder is 92
Strike the previous value
The new value is 92
Move to the next bin (denomination: 50)
Pick 1 coin from the bin
The total amount picked from the bin is 50
The remainder is 42
Move to the next bin (denomination: 20)
Pick 2 coins from the bin
The total amount picked from the bin is 20
The remainder is 2
Move to the next bin (denomination: 10)
Pick 0 coins from the bin
The total amount picked from the bin is 0
The remainder is 2
Move to the next bin (denomination: 10)
Pick 0 coin from the bin
The total amount picked from the bin is 0
The remainder is 2
Move to the next bin (denomination: 5)
Pick 0 coin from the bin
The total amount picked from the bin is 0
The remainder is 2
Move to the next bin (denomination: 2)
Pick 1 coin from the bin
The total amount picked from the bin is 2
The remainder is 0
Done
Implementing this in Python
Python is an amazingly clear language, and makes this sort of tasks easy. So let's try to translate our algorithm to Python.
The toolbox
Assuming you're using Python 3.x, you'll need to know some operators:
The integer division operator (//): If you divide with just a single slash, you'll get the "real division" (e.g. 3 / 2 == 1.5), but if you use a double slash, you'll get the "integer division (e.g. 3 // 2 = 1)
The modulo operator (%): As explained above, this operator returns the remainder of a division (e.g. 7 % 4 == 3)
Used together, these operators will give you what you need on each step:
292 // 200 == 2
292 % 200 == 92
92 // 100 == 0
92 % 100 == 92
...
One useful characteristic of Python is that you can perform a "multiple assignment": You can assign multiple values to multiple variables in one single step:
# Initialize the value:
value = 292
# Initialize the denomination:
denomination = 200
# Calculate the amount of coins needed for the specified denomination
# and get the remainder (overwriting the value), in one single step:
coins, value = value // denomination, value % denomination
# ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
# | The remainder
# The number of coins
# (using integer division)
With this knowledge, we can write the solution:
Correcting your code
Remember: Read all of the above before revealing the solutions below.
def selectCoins():
twopound = 200
onepound = 100
fiftyp = 50
twentyp = 20
tenp = 10
fivep = 5
twop = 2
onep = 1
a = 0
b = 0
c = 0
d = 0
e = 0
f = 0
g = 0
h = 0
money = int(input('Enter how much money you have in pence')) # Example: 292
# Calculate the number of coins needed and the remainder
# The remainder will "overwrite" the value previously held in the "money" variable
a, money = money // twopound, money % twopound # a = 1, money = 92
b, money = money // onepound, money % onepound # b = 0, money = 92
c, money = money // fiftyp, money % fiftyp # c = 1, money = 42
d, money = money // twentyp, money % twentyp # d = 2, money = 2
e, money = money // tenp, money % tenp # e = 0, money = 2
f, money = money // fivep, money % fivep # f = 0, money = 2
g, money = money // twop, money % twop # g = 1, money = 0
e, money = money // onep, money % onep # e = 0, money = 0
print(a,b,c,d,e,f,g,h)
This solution uses both integer division and remainder to perform the calculations.
Let's do it the right way: with a loop
Let's face it: the above code is verbose. There must be a better way... and there is! Use a loop.
Consider the algorithm: you repeat the steps jumping from one bin to the next and getting the number of coins you need and the remainder. This can be written in a loop.
So, let's add a list to our toolbox:
denominations = [200, 100, 50, 20, 10, 5, 2, 1]
And let's store the results of each step in a second list:
coins = [] # We'll use the '.append()' method to add elements to this list
So, starting in the first "bin":
n, money = money // denominations[0] , money % denominations[0]
coins.append(n)
Let's put this in a loop:
def select_coins_v2():
denominations = [200, 100, 50, 20, 10, 5, 2, 1]
coins = []
money = int(input('Enter how much money you have in pence'))
for i in range(len(denominations)):
n, money = money // denominations[i], money % denominations[i]
coins.append(n)
print(coins)
And that's it!
Another improvement: get the denomination only once and use it twice
Notice that the code above still has an issue: you read denominations twice. It would be nice if the denomination value could be read only once.
Of course, there is a way:
def select_coins_v3():
denominations = [200, 100, 50, 20, 10, 5, 2, 1]
coins = []
money = int(input('Enter how much money you have in pence'))
for d in denominations: # 'd' will hold the value of the denomination
n, money = money // d, money % d
coins.append(n)
print(coins)
As a friend of mine says: "Fast, precise and concise; not slow, difuse and confusing"
TL;DR
In Python 3.x, the "integer division" operator is // and the remainder (modulo) operator is %.
You can perform multiple assignement in a single line of code:
a, b = 1, 2
You can store the denominations in a list:
denominations = [200, 100, 50, 20, 10, 5, 2, 1]
You can read from the denominations list and get both the integer division and the remainder in a single step:
n, money = money // denominations[0], money % denominations[0]
You can write a loop that does all of the above:
for d in denominations: n, money = money // d, money % d
Bonus: Use a dictionary
What if I want to print both the denominations and the number of coins of each denomination I used? You can traverse both lists with a loop, but you can also keep it simple by using a dictionary:
def select_coins_v4():
denominations = [200, 100, 50, 20, 10, 5, 2, 1]
coins = []
money = int(input('Enter how much money you have in pence'))
for d in denominations: # 'd' will hold the value of the denomination
n, money = money // d, money % d
coins.append(n)
number_of_coins = dict(zip(denominations, coins))
print(number_of_coins)
Python offers a great deal of flexibility. Feel free to try different ways of getting what you need... and choose the easier one.
Hope this helps.
the cool thing about using real denominations is that the greedy solution will always find the optimal solution ... this stops holding true with weird denominations... but these problems are always easiest if you break them into parts
def get_one_change(amt_due):
# find how many of the largest denomination that you can use is
# ie for 60 = 1x50p is the count and number of largest
# for 4 = 4x1p ; 21 = 2x10p ; etc
return pence,count # ie 50,1 would be 1 50p coin
once you have this you just need to repeatedly call it and adjust your result until you have no change due
def get_change(amount_due):
changes_due = [] # to keep track
while amount_due:
pence,qty_of_coin = get_one_change(amount_due)
changes_due.append({"coin":pence,"qty":qty_of_coin})
amount_due = amount_due - (pence*qty_of_coin)
return changes_due
now you can just call your get_change method with your users input
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 want to calculate cost of water expense of households constrained by certain thresholds:
Basically the thresholds are as follows:
for the first 0-10 m3 it will cost $10
for the subsequent 11-20 m3 it will cost $20
for the subsequent 21-30 m3 it will cost $30
for the subsequent >30 m3 it will cost $40
For example, if a household uses 40 m3 of water, it will be charged:
(10*10) + (10*20) + (10*30) + (10*40) = $1000
if a household uses 23 m3 of water, it will be charged:
(10*10) + (10*20) + (3*30) = $390
The only way I can think of is using if-conditionals. And I don't think that's the best way to calculate this.
You could also use a dictionary:
dic = {0:(0,10), 1:(100,20), 2:(300,30), 3:(600,40)}
Then you only need once an if-statement.
def costs(vol):
interval = vol/10 # vol must be positive int
if interval in dic:
price = dic[interval]
return price[0] + price[1]*(vol-price[1] + 10)
else:
return dic[3][0] + dic[3][1]*(vol-dic[3][1] + 10)
You can use the fact that the cost function is piecewise linear. Thus, you have to find out at which interval you are and use this as the key to your dictionary, where you save the cost at the beginning of each interval aswell as the increase of cost.
First define a list which will hold these limits in (difference in volume, price) format:
lst = [(10, 10), (10, 20), (10, 30), (None, 40)]
Note that the order matters. Here None stands for "no limit". And now the function:
def get_money(volume):
if volume <= 0:
return 0
total = 0
for diff, price in lst:
if volume == 0:
break
if diff is None:
total += volume * price
volume = 0
elif volume > diff:
total += diff * price
volume -= diff
else:
total += volume * price
volume = 0
return total
BTW: There is no way to solve this without using if. :)
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!