What is an efficient way to calculate cost constrained by thresholds - python

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. :)

Related

is it possible to recheck an index in a list in the same loop again? python

What I'm trying to do is to make the loop goes to every index in the "investments" list then checks if it's equal to "aMoney" variable or not. if the statement is True then the same index of "investments" in "revenue" list data will be added to "totalMoney" variable and goes back again to do next.
My problem is that I want the loop to go back to the False statements to recheck if it's True again and add it to the "totalMoney" variable.
what's happening here is the loop will skip index[0] because 2040 >=/ 3000. but after many loops the condition will be True if we do it again with the new number.
note: Sorting won't work because index[] in first list must go with what same index[] in the second list, no changes.
here is my full code:
numOfProjects = int(7)
aMoney = int(2040)
investments = [3000,2040,3040,5000,3340,4000,7000]
revenue = [500,1000,300,450,2010,650,1500]
totalMoney = int()
totalMoney = aMoney
for j in range(len(investments)):
if(totalMoney >= investments[j]):
totalMoney += revenue[j]
investments[j] + 1
revenue[j] + 1
totalMoney -= aMoney
print("The profit is $", totalMoney)
the output of this code will be $3960
in papers, it should be $4910
because, first round should be
"2040 + 1000 + 300 + 2010 + 650" = 6000
then we go back at what we left
"6000 + 500 + 450" = 6950
6950 - 2040 = 4910
I tried my best explaining the idea, hope it's clear
In python, we don't need to initialise int variables as int(number), just declaring them with the corresponding value suffices. So instead of aMoney = int(2040), just aMoney = 2040 works.
You can use zip to sort the investments and revenue together. Here's a simplified code which does what you need -
initialMoney, currentMoney = 2040, 2040
investments = [3000,2040,3040,5000,3340,4000,7000]
revenue = [500,1000,300,450,2010,650,1500]
data = sorted(zip(investments, revenue))
print(data)
for investment, revenue in data:
if investment <= currentMoney:
currentMoney += revenue
netProfit = currentMoney - initialMoney
print("The profit is $", netProfit)
Output:
[(2040, 1000), (3000, 500), (3040, 300), (3340, 2010), (4000, 650), (5000, 450), (7000, 1500)]
The profit is $ 4910
What you see printed just before profit, is the (investment, revenue) sorted by investment.

Yearly Interest on house and deposit

Suppose you currently have $50,000 deposited into a bank account and the account pays you a constant interest rate of 3.5% per year on your deposit. You are planning to buy a house with the current price of $300,000. The price will increase by 1.5% per year. It still requires a minimum down payment of 20% of the house price.
Write a while loop to calculate how many (integer) years you need to wait until you can afford the down payment to buy the house.
m = 50000 #money you have
i = 0.035 #interest rate
h = 300000 #house price
f = 0.015 #amount house will increase by per year
d= 0.2 #percent of down payment on house
y = 0 #number of years
x = 0 #money for the down payment
mn = h*d #amount of down payment
while m <= mn:
m = (m+(m*i)) #money you have plus money you have times interest
y = y + 1 #year plus one
mn = mn +(h*f*y)
print(int(y))
The answer you should get is 10.
I keep getting the wrong answer, but I am not sure what is incorrect.
You can simplify the code by using the compound interest formula.
def compound_interest(amount, rate, years):
return amount * (rate + 1) ** years
while compound_interest(m, i, y) < d * compound_interest(h, f, y):
y += 1
If you are allowed to do without the while loop, you can resolve the inequality after the years y.
So you get this code snippet:
import math
base = (i + 1) / (f + 1)
arg = (d * h) / m
y = math.ceil(math.log(arg, base))

How can I improve the time complexity of this algorithm for finding the max stock price?

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

algorithm that calculates a minimum amount to be paid to pay off a balance in 12 months

I'm struggling with a problem that calculates the minimum fixed monthly payment needed in order pay off a credit card balance within 12 months. By a fixed monthly payment, I mean a single number which does not change each month, but instead is a constant amount, multiple of 10 and the same for all months, that will be paid each month. ( it is possible for the balance to become negative using this payment scheme, which is OK)
So as input I have
original_balance = 3329
annualInterestRate = 0.2
From this I'm calculating the followings :
after_12_months_interest = original_balance
monthlyInterestRate = round(annualInterestRate/12.0,2)
monthly_payment = 10
total_paid = 0
for i in range(0,12):
after_12_months_interest = after_12_months_interest + (annualInterestRate/12.0)*after_12_months_interest
while total_paid < after_12_months_interest:
new_balance = 0
unpaid_balance = original_balance - monthly_payment
total_paid = 0
for i in range(0, 13):
total_paid = total_paid + monthly_payment
if total_paid < after_12_months_interest:
monthly_payment = monthly_payment + 10
print "Lowest Payment: ", monthly_payment
My problem I have is that I end up having a monthly_payment just a little more than I should have it. In this case the return for the monthly_payment is 320 instead of 310. For all use cases I've tried the monthly_payment it's slightly more than it should be.
Anyone can give me a hint or an idea on what I'm doing wrong please. Thank you
Mandatory one-liner
from itertools import count
print(next(payment for payment in count(0, 10)
if sum(payment*(1+monthly)**i for i in range(12)) > original_balance*(1+annual)))
What this does:
next takes the first element of an iterator.
count tries values from 0 to infinity (only each time next is called, and only until a value is returned)
sum(payment*(1+monthly)**i for i in range(12)) That's the combined value of the payments. Each payment is worth itself plus all the saved interests (the earlier you repay, the less interest you'll owe later)
original_balance*(1+annual) is indeed the total value if nothing is repayed.
Alternative
print(next(payment for payment in count(0, 10)
if reduce(lambda x,_:(x - payment)*(1+monthly), range(12), original_balance) <= 0))
This one computes the combined remainder of the debt by reduceing the original_balance 12 times.

Python: What could I do differently to slim down my Python code?

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!

Categories

Resources