What is the reason this while loop does not generate output? - python

I am new to python and trying to learn by doing small projects.
The problem
Strontium-90, a radioactive element that is part of the fallout
from nuclear explosions, has a half-life of 28 years. This means that a given quantity
of strontium-90 will emit radioactive particles and decay to one-half its size every
28 years. How many years are required for 100 grams of strontium-90 to decay to less
than 1 gram?
My Input code:
full_life = int(100)
while full_life < int(1):
full_life -= 0.5*full_life
year +=28
print("The decay time is:",year)
On running the above, no output is generated. why is this the case? what am i missing?

while loop logic is reversed should be:
while full_life > int(1):

Right away you have made full_life 100, checking the condition in the while loop comes out as false right away (since 100 < 1 is false). Since it is false you will not go through the loop. I imagine what you're looking for is to switch the sign from < to >
full_life = int(100)
while full_life < int(1):
do stuff
to
full_life = int(100)
while full_life > int(1):
do stuff

After you reverse your while loop logic like this:
while full_life>1:
do something
The other problem will occur.
You didn't define 'year'
you shuld do this:
year=0

full_life represents the amount of strontium you have. Initially, you have 100 grams, and are starting at year 0.
year = 0
full_life = 100
You want to decrease full_life until it is less than 1 gram, which means you execute the body of the loop as long as full_life is greater than or equal to 1.
while full_life >= 1:
full_life /= 2
year +=28
print("The decay time is:",year)
Each time the condition is true, you divide full_life in half, and increment year by 28 years. Once the loop has completed, you can output the total number of years before the condition because false.
Note this is not entirely correct, as the mass doesn't stay constant for 28 years, then immediately drop by a factor of 2. This is most obvious if you start with 1 gram of strontium. It doesn't take 28 years for any of it to decay.
What you really need to do is pick a granularity (hour, day, month, whatever), and figure out from the half-life how much of the sample will decay in that amount of time. Then you can decrease full_life by smaller amounts while increasing time likewise to find out when full_life finally drops under 1.

Related

How does the python interpreter know that only specific variables in the equation are to be multiplied by the number of days?

I am working on practice examples on edabit using python. One of the examples I am stuck on is as follows:
Create a function that takes the number of daily average recovered cases recovers, daily average new_cases, current active_cases, and returns the number of days it will take to reach zero cases.
def end_corona(recovers, new_cases, active_cases):
end_corona(4000, 2000, 77000) ➞ 39
end_corona(3000, 2000, 50699) ➞ 51
end_corona(30000, 25000, 390205) ➞ 79
Below is one of the correct solutions that I am not quite sure how the interpreter knows that only "new_cases" and "recovers" are supposed to be multiplied by the number of days (d). Somehow this solution does work but can someone explain to me why and how?
def end_corona(recovers, new_cases, active_cases):
days = 0
while active_cases > 0:
days += 1
active_cases = active_cases-recovers+new_cases
return days
The function is running in a loop. So in each iteration it subtracts the daily average recovered cases from the active cases and adds the daily average new cases and increase the number of days by one. It runs until the active cases are not zero and returns the total number of days it would take to get to zero cases.

Code not calculating final count as per rate correctly

I was trying to solve the following Problem Statement:
In the city A, there is a disease x, spreading at the rate of 2. The
rate doubles after every 3 days.
If 2 people were initially infected with the disease, how many people
will be affected in total after 100 days?
(Hint: Ni+1 = Ni + a*Ni *t where Ni is the ith day patients and Ni+1
is patients the day after that, a is the increase rate and t is the
number of days.)
This is the Python code I tried to solve the statement:
rate = 2
rate_of_rate = 2 # the rate doubles every gap days
gap = 3
initially_infected = 2
final_day = 100
infected = initially_infected
days_passed = 1
while days_passed != final_day:
if days_passed%gap == 0:
rate *= rate_of_rate
infected = infected * rate
days_passed += 1
print(infected)
The answer expected is: 658781418
And the answer I'm getting is:
7387586092700242099654546576830696772603866567292789055868426442323956818125567473217880665869221255368279336978185916233370357196371072076345487974033022845153783727077340269105240653596212209328236829977000561171160601353019714984950312214004440228069460097961675499715690703175560410535127557079386864191774441606293810308368351268196693882638167250873667663250863266951807800784887663781068841491777971210302562177144021123949168116897834247743963522769738506629596576834286879022276623596962844306405686165635072
Where am I doing wrong?
P.S. > I am also not able to understand where the formula is incorporating rate_of_rate
Your program is perfectly correct.
Even if the disease would be
spreading at a constant rate 2, and
initially would be infected only 1 man, and
instead of 100 days would be only 63 days,
your task would be an analogy of well-known “Wheat and chessboard problem” with the result as large as 18,446,744,073,709,551,615.
From the Wikipedia “Wheat and chessboard problem”:
If a chessboard were to have wheat placed upon each square such that
one grain were placed on the first square, two on the second, four on
the third, and so on (doubling the number of grains on each subsequent
square), how many grains of wheat would be on the chessboard at the
finish?

Calculate 20 minute average & best 20 minute average

I'm trying to learn Python, with a project where I'm reading data from a bike power meter. Right now I'm just calculating the average power from start to finish, by adding each power reading to a total sum variable, and dividing it with the number of readings.
I'd like to calculate the average power for 20 minutes, and if possible, keep calculating the 20 minute average each 30 seconds after the first 20 minutes, comparing to the previous value and storing it if it's higher than the last, so in the end I could have the higher 20 minute average power that I held during a one hour workout, no matter where it happened in that hour, for example.
Data from the power meter is event based, as far as I can tell it's not a regular intervals, but definitely once a second or faster.
This is the base of my code so far:
def average_power(power, count):
global PM1_sumwatt
global PM1_avgwatt
PM1_sumwatt = PM1_sumwatt + power
PM1_avgwatt = PM1_sumwatt / count
PM1_avgLog = open(PM1_avgFile, 'w')
PM1_avgLog.write("<div id=\"pwr\"> %d W</div>" % (PM1_avgwatt))
PM1_avgLog.close()
def power_data(eventCount, pedalDiff, pedalPowerRatio, cadence, accumPower, instantPower):
global PM1_avgcount
if WCORRECT1: calibpower = instantPower+(CF1w)
else: calibpower = instantPower*(CF1x)
power_meter.update(int(calibpower))
PM1_avgcount = PM1_avgcount + 1
average_power(int(calibpower), PM1_avgcount)
power = BicyclePower(readnode, network, callbacks = {'onDevicePaired': device_found,
'onPowerData': power_data})
# Starting PM readings
power.open(ChannelID(PM1, 11, 0))
Not quite sure how to tackle this! Any help or pointer is much appreciated!
if you are reading data in real time, I assume you are reading the data in a while loop:
sum = 0
number_of_readings = 0
while True: # forever
new_value = input() # here I read from the console input
sum += new_value
number_of_readings += 1
average = sum/number_of_readings
print(average)
Here I type a number in the console and press enter to simulate your bike power meter.
>>> 1
1.0
>>> 3
2.0
>>> 4
2.6666666666666665
>>> 2
2.5
Now, if you wants to make a moving average, you must store the readings that you wants to average. This is because you want to remove them later, when they will be too old. A list is perfect for that:
Here is a solution averaging the last n readings:
n = 2
Last_n_readings = []
while True: # forever
# add a new reading
new_value = int(input()) # here I read from the console input
Last_n_readings.append(new_value)
# discard an old one (except when we do not have enough readings yet)
if len(Last_n_readings) > n:
Last_n_readings.pop(0)
# print the average of all the readings still in the list
print(sum(Last_n_readings) / len(Last_n_readings))
Which gives:
>>> 1
1.0
>>> 3
2.0
>>> 4
3.5
>>> 2
3.0
Note that lists are not very efficient when removing elements near the start, so there are more effective ways to do this (circular buffers), but I try to keep it simple ;)
You could use this by guessing how many readings/seconds you have and choose n so you average over 20 minutes.
If you want to truly average all the result which are less than 20 minutes ago, you need to not only record the readings but also the times when you red them, so you can remove the old readings wen they get more than 20 minutes old. If this is what you need, tell me and I will expand my answer with an example.
You can use pandas dataframe to store the power output for each instance.
Considering that you receive a value each 30 second, you can store them all in data frame.
Then calculate a 40 data point moving average using rolling function in python.
Take the max value you get after the rolling function, this would be your final result.
refer this for doc : https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html

Bisect Search to choose best savings rate

Hi I would like some help with this questions set as one of the problems on the MIT OCW Computer Science and Python Course. I know people have asked similar questions and I have found useful posts such as Bisection search code doesnt work but I am still stuck!
I have struggled with this problem for many days and tried to tackle it in different ways and failed in all ways. If at all possible, could somebody just hint at where I am going wrong, rather than telling me the answer. I would like to solve this problem for myself with bit of help.
For reference, the question is part C, here: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/assignments/MIT6_0001F16_ps1.pdf
As I have been struggling, I have broken down this task into an overall aim and then into steps to solve the problem.
Aim: try to find the best rate of savings to achieve a down payment on a $1M house in 36 months ##Steps to solve the problem:
1) make a guess on the savings rate, which is the average of the 0 and 1000
2) calculate how that would grow over 36 months
3a) if amount reached goes over 25% of $1m within 36 months, then a lower savings rate should be the new guess
...max=guess (old guess) and min=0 and update the guess (average of high and low)
...run the calculation in step 2 with the new guess
3b) if amount does not reach 25% of $1m within 36 months, then a higher savings rate should be the new guess
...min=guess (old guess) and update the guess (average of high and low)
...run the calculation in step 2 with the new guess
3c) if amount reaches 25% of $1m within the 36th month deadline then quit and record the savings rate as the best guess.
For simplicity: assume no interest and assume wages remain same
So here's my code with the current effort at solving this. (it leads to the "guess" variable tending to 0 and then infinitely looping)
total_cost=1000000 #cost of house
portion_down_payment=0.25 #fraction of cost needed for downpayment on house
downpayment=total_cost*portion_down_payment
starting_annual_salary=float(input("Enter the starting salary: "))
low=0
high=1000
bisect_steps=0
month=1 #set as 1 because the first calculation will occur in month 1
guess=(low+high)//2
current_savings=0
def calSavings(current_savings,monthly_salary,guess,month):
while month<37:
monthly_savings=monthly_salary*(guess/1000)
current_savings+=monthly_savings
month+=1
return(current_savings)
current_savings=calSavings(current_savings,monthly_salary,guess,1)
while True:
current_savings=calSavings(current_savings,monthly_salary,guess,1)
if current_savings>downpayment and month<=35: #if amount reached goes over 25% of $1m within 36 months
#a lower savings rate should be the new guess
high=guess #max=guess (old guess) and min=0 and update the guess
bisect_steps+=1
guess=(low+high)//2
print("The guess was too high, so the new lower guess is",guess," This is bisect step",bisect_steps)
continue #send new guess up to beginning of while loop to calculate
elif current_savings<downpayment and month>=36: #if amount does not reach 25% of $1m within 36 months
low=guess
bisect_steps=+1
guess=(low+high)//2
print("The guess was too low, so the new higher guess is",guess," This is bisect step",bisect_steps)
continue #send new guess up to beginning of while loop to calculate
elif current_savings>=downpayment and month==36: #if amount reaches 25% of $1m in the 36th months then quit
# record the savings rate as the best guess
print("The best savings rate is ",guess/100,"%, the amount saved was ",current_savings," in ",month," months")
break #break out of while loop
I know other people have asked similar questions (I have looked at those answers and still not solved my problem) but more than an answer I want help on HOW to solve this problem.
Update
The reason why your loop isn't stopping is because you aren't giving it enough time. What you are forgetting is that you're dealing with the decimal type. Using the == with decimal values is always dangerous. The decimal type is accurate (by default) to 28 places, which means you're trying to find an extremely good approximation for this problem, because only when it's correct to 28 decimals will (current_savings>downpayment or current_savings<downpayment) evaluate to False invoking your exit condition.
Basically, the issue that's causing your problem is that even when you eventually get the estimate of $1,000,000.0000000001, python says this is not equal to $1,000,000.0000000000, so it keeps going until it gets the next 0, but then it just adds another zero and so on. This will continue for a very very long time and in rare cases might never stop due to the fact that not all decimal numbers can be stored as binary numbers (1,000,000 is not among those cases).
So, how do we solve this? There are two options. The easiest one is to ignore cents and just cast your comparison values to int, this will make sure that any value off by a fraction of a dollar will be accepted. The other options is to create a range of accepted answers. Say for example, I'd like to save EXACTLY $1 million in those 36 months, but that isn't likely to happen. So, instead, I'll settle for any amount in the range $1,000,000.00 - $1,000,010.00 (for example). This way, we ensure that any guess that is way too high will get rejected and only a very limited amount of guesses are accepted.
Regardless of which route you go, it is generally a good idea to put the exit condition of an infinite loop to the top, this way you guarantee that it will always be evaluated.
My suggestion would be to write a function like this and use that for your condition to exit the loop (which you would place at the top):
def are_decimals_equal(a, b):
accuracy = 0.0001
return abs(a-b) < accuracy
This will consider 0.00009 (and all decimals less than that) to be equal to 0.0000.
Original
First off, just as a note, what you're doing is not called bisection, it's called binary search.
Now to the problem, you are never altering the value of month in your main loop. This means, as soon as current_savings>downpayment evaluates to False, your program will go into an infinite loop as none of the conditions after it could evaluate to True as month>=36 will always be False.
From what I can see, the second part of your conditions in the if/elif statements is unnecessary, your calSavings will always compute 36 months worth of savings, never more, never less. Thus, if you remove that condition from your if/elif statements, your program will eventually stop and at that point it should settle on the correct answer.
Lastly, the reason why you're seeing 0 as the output is your division at the end. If you do print(typeof(guess)) you will see it is an integer, 100 is also an integer, thus this division will result in some value like 0.3123 which will get truncated to 0. Change your output to float(guess/100) and this will go away.
I hope it's okay for me to provide an answer to my own question here - though it's not a perfect answer.
The results the code produces seem plausible.
total_cost=1000000 #cost of house
portion_down_payment=0.25 #fraction of cost needed for downpayment on house
downpayment=total_cost*portion_down_payment
starting_annual_salary=float(input("Enter the starting salary: "))
monthly_salary=starting_annual_salary/12
low=0
high=1000
binary=0
month=1 #set as 1 because the first calculation will occur in month 1
guess=(low+high)//2
current_savings=0
tolerance=500
def calSavings(current_savings,monthly_salary,guess,month):
while month<37:
monthly_savings=int(monthly_salary*(guess/1000))
current_savings+=monthly_savings
month+=1
return(current_savings)
current_savings=calSavings(current_savings,monthly_salary,guess,1)
while True:
if abs(current_savings-downpayment)<=tolerance: #if the difference between the current savings and downpayment is less than $500
# record the savings rate as the best guess
print("The best savings rate is ",guess/10,"%, the amount saved was $",current_savings," in 36 months")
break #break out of while loop
elif (current_savings-downpayment)>tolerance: #if amount reached goes over 25% of $1m within 36 months
#a lower savings rate should be the new guess
high=guess #high=guess (old guess) and low=low (stays same) and update the guess
binary=binary+1
guess=(low+high)//2
print("The guess was too high, so the new lower savings rate is",guess/10,"%. This is binary-search step",binary)
current_savings=calSavings(0,monthly_salary,guess,1)
continue #send new guess up to beginning of while loop to check if conditionals
elif (downpayment-current_savings)>tolerance: #if amount does not come to within tolerance amount of 25% of $1m within 36 months
low=guess #to make the guess higher, make low=guess (old guess) and high stay the same
binary=binary+1
guess=(low+high)//2
print("guess is ",guess)
if guess>=990: #check if the savings rate guess is getting too high
print("Your wages are too low. You can't save up enough")
break #exit the while loop because conditions will never be met
print("The guess was too low, so the new higher savings rate is",guess/10,"%. This is binary-search step",binary)
current_savings=calSavings(0,monthly_salary,guess,1)
continue #send new guess up to beginning of while loop to check over the conditionals
The tolerance for an acceptable answer is within $500, but if I lower that to $50, I end up in a seemingly infinite loop again, where the guess and the low end up being the same. I am happy that I've made some apparent progress, but baffled that I can't lower the tolerance without it going haywire again.
BTW, I didn't want to seem like I ignored Nick's comments about making the variables into floats, but I explained why I worked in integers in my comment - does that seem correct?

Python Blackjack Game how to carry a variable in a while loop

I'm still deep into the making of my Blackjack game in Python, my aim is to allow the computer to 'twist' (Take another card) until the total value is greater than 15. From then onwards I want it so that as the total of the cards gets higher, the chance of the computer taking another card gets smaller just like a human (taking a risk if the number isn't TOO big).The following code occurs after the computer has been dealt two values.
if (computerCardValueTotal < 15):
print ("The computer has decided to twist")
ranCompCard3 = random.choice(availableCards)
computerCardValueTotal = ranCompCard + ranCompCard2 + ranCompCard3
if (computerCardValueTotal < 15):
print ("The computer has chosen to twist again")
My aim is to have this piece of code loop if the total value is less than 15 so that the computer twists until it is over 15. I have considered using a while loop but I'm unsure on how to carry the current total to the start of the loop so that the next card value is added to the current total. Does anyone have a solution they could help me out with?
Also, secondly, it's not the biggest of the two issues in this question, but how would you recommend doing the computer so that the chance of it twisting again is smaller as the total value of cards get bigger? For example, for if the value of the cards totals at 17, there's a 1/10 chance of the computer twisting but if the total value of the cards is 19 the chance of the computer twisting is 1/20.
All valuable help will be voted up and as always, thanks in advance! :)
You have hobbled yourself by making e.g. ranCompCard, ranCompCard2, ranCompCard3, ... when a list would make life much easier:
compCards = [ranCompCard, ranCompCard2]
while sum(compCards) < 15:
compCards.append(random.choice(availableCards))
For adjusting the probability of picking another card, you could do something like:
while True:
gap = 21 - sum(compCards)
if gap > 0 and random.random() < (1 / (40 / float(gap))):
compCards.append(random.choice(availableCards))
else:
break
Note that this is a linear relationship between gap and the probability of picking another card:
>>> for card_sum in range(15, 21):
print card_sum, 1 / (40 / float(21 - card_sum))
15 0.15
16 0.125
17 0.1
18 0.075
19 0.05
20 0.025
but you can adjust the equation as needed.
While you're editing your code, take a look at (and consider following) the official style guide; variable names should be lowercase_with_underscores.
Simply reassign the value when it changes.
while computerCardValueTotal < 15:
# other code
computerCardValueTotal = <new value>

Categories

Resources