Simple while loop wont break - python

I'm completely new to python and coding in general, it's probably some ridiculously obvious error but I can't figure out why this while loop won't break.
I'm trying to find the value of t when y is equal to 0. When changing t manually, y is less than 0 when t is just under 2.5.
Unfortunately I don't have anyone else I can ask for right now. Thanks!
t = 0
y0 = 1.8
v0 = 15.0
theta0 = 0.785398163
vy0 = v0*sin(theta0)
ay0 = -9.81
y = y0 + vy0*t + 0.5*ay0*t**2
while y > 0:
print(t)
t += 0.25
if y < 0:
break
print(t)

You need to include your equation in the loop so that the value of y changes each time you increase t. Like this:
while y > 0:
y = y0 + vy0*t + 0.5*ay0*t**2
print(t)
t += 0.25
if y < 0:
break print(t)

You could restructure your loop as follows:
t = 0
y0 = 1.8
v0 = 15.0
theta0 = 0.785398163
vy0 = v0*sin(theta0)
ay0 = -9.81
y = 1 # dummy start value
while y >= 0:
y = y0 + vy0*t + 0.5*ay0*t**2
print("t={}, y={}".format(t,y))
t += 0.25
This would then also display all of the values as it loops, so you can see it working:
t=0, y=1.8
t=0.25, y=4.145087928395659
t=0.5, y=5.8770508567913184
t=0.75, y=6.9958887851869775
t=1.0, y=7.501601713582638
t=1.25, y=7.394189641978297
t=1.5, y=6.673652570373955
t=1.75, y=5.339990498769618
t=2.0, y=3.3932034271652753
t=2.25, y=0.8332913555609345
t=2.5, y=-2.3397457160434065
By adding a dummy start value, it ensures that the loop will be entered, and avoids the need to also add a break statement within the loop.
From these results you can see there is a solution somewhere between 2.25 and 2.5. You could then change your start value to say t = 2.25 and change the increment to t += 0.01 and you would see a slightly more accurate estimation.

while y > 0
at this line loop will start when y > 0, but then you didn't change the value of y during while loop then how if y < 0 this statement will execute.
And your y = 1.8 after calculating y = y0 + vy0*t + 0.5*ay0*t**2 so it is an infinite loop as y > 0 is all time true.

As you can see from other answers, you need to move your equation to inside the while loop.
In programming the assignment operator (this thing: =) does not create relationships between variables. It (usually) simply takes what is on the right and puts it in what is on the left.
Consider this code:
y = 0
x = y+1
y = 33
print x
It will print out 1.
y+1 gets evaluated to 0+1 then 1.
Then x gets set to the integer value of 1.
Changing y will not do anything because x was set to the specific value of that expression at that specific time, not to a reference.
You might have noticed the "usually" in my definition of the assignment operators, that's because references do exist in programming and specifically in python. For example this happens when you set a variable equal to an uninstantiated class, but at this point understanding that is not necessary.

Related

How do you check if something has declined by 10% without an if statement?

I am given two numbers x and y in Python, and I'm asked if y represents a 10% decrease from x.
What is the best way of doing this without an if-statement? I am looking to vectorize this operation, so it'd be good to have a branchless form.
The verbose check would be:
def check(x,y):
if x < 0:
return y < x*1.1
else:
return y < x*0.9
I considered
def check(x,y):
return y < x * (1.1 if x < 0 else 0.9)
but that's just an inline if-statement.
If I understand the question correctly, I think this should work:
def ten_percent_decrease(x: int, y: int):
"""
check if y is 10% less than x
"""
return (x - y) / abs(x) > 0.1
Note: the code breaks if you give 0 for x. I'm not sure what you want the expected output to be in that case, but you can adjust it accordingly.

What does the print mean and its execution?

Can someone help me understand what this code does?
The task is that I need to describe what the program does and what the print tells us. I'm really stuck and could use some help!
from random import random
wrong = 0; N = 100000
for i in range(N):
x = random(); y = random(); z = random()
res1 = (x + y) * z
res2 = x*z + y*z
if res1 != res2:
wrong += 1
x0 = x; y0 = y; z0 = z
notequal1 = res1
notequal2 = res2
print (100. * wrong/N)
print (x0, y0, z0, notequal1 - notequal2)ยจ
the code prints out:
30.825
0.7274024508251914 0.7713456905186189 0.06463959172321276 1.3877787807814457e-17
Basically what your code does is simple
For 100000 times , it does the following :-
It generates 3 random numbers
It performs two operations (one is adding x and y and then multiplying by z. The other one multiplies x with z and y with z
Then it performs a comparison if both are not equal or not . If they are not equal , then it increases the counter (aka number of times wrong) by 1 and then stores the value
Lastly , once it has executed 100000 times , it prints out the percentage of wrong (how many times in that run it was wrong aka not equal) and also what were the last value and the differences
Hope this helps you out in understanding the code.

Trapezoid Rule in Python

I am trying to write a program using Python v. 2.7.5 that will compute the area under the curve y=sin(x) between x = 0 and x = pi. Perform this calculation varying the n divisions of the range of x between 1 and 10 inclusive and print the approximate value, the true value, and the percent error (in other words, increase the accuracy by increasing the number of trapezoids). Print all the values to three decimal places.
I am not sure what the code should look like. I was told that I should only have about 12 lines of code for these calculations to be done.
I am using Wing IDE.
This is what I have so far
# base_n = (b-a)/n
# h1 = a + ((n-1)/n)(b-a)
# h2 = a + (n/n)(b-a)
# Trap Area = (1/2)*base*(h1+h2)
# a = 0, b = pi
from math import pi, sin
def TrapArea(n):
for i in range(1, n):
deltax = (pi-0)/n
sum += (1.0/2.0)(((pi-0)/n)(sin((i-1)/n(pi-0))) + sin((i/n)(pi-0)))*deltax
return sum
for i in range(1, 11):
print TrapArea(i)
I am not sure if I am on the right track. I am getting an error that says "local variable 'sum' referenced before assignment. Any suggestions on how to improve my code?
Your original problem and problem with Shashank Gupta's answer was /n does integer division. You need to convert n to float first:
from math import pi, sin
def TrapArea(n):
sum = 0
for i in range(1, n):
deltax = (pi-0)/n
sum += (1.0/2.0)*(((pi-0)/float(n))*(sin((i-1)/float(n)*(pi-0))) + sin((i/float(n))*(pi-0)))*deltax
return sum
for i in range(1, 11):
print TrapArea(i)
Output:
0
0.785398163397
1.38175124526
1.47457409274
1.45836902046
1.42009115659
1.38070223089
1.34524797198
1.31450259385
1.28808354
Note that you can heavily simplify the sum += ... part.
First change all (pi-0) to pi:
sum += (1.0/2.0)*((pi/float(n))*(sin((i-1)/float(n)*pi)) + sin((i/float(n))*pi))*deltax
Then do pi/n wherever possible, which avoids needing to call float as pi is already a float:
sum += (1.0/2.0)*(pi/n * (sin((i-1) * pi/n)) + sin(i * pi/n))*deltax
Then change the (1.0/2.0) to 0.5 and remove some brackets:
sum += 0.5 * (pi/n * sin((i-1) * pi/n) + sin(i * pi/n)) * deltax
Much nicer, eh?
You have some indentation issues with your code but that could just be because of copy paste. Anyways adding a line sum = 0 at the beginning of your TrapArea function should solve your current error. But as #Blender pointed out in the comments, you have another issue, which is the lack of a multiplication operator (*) after your floating point division expression (1.0/2.0).
Remember that in Python expressions are not always evaluated as you would expect mathematically. Thus (a op b)(c) will not automatically multiply the result of a op b by c like you would expect with a mathematical expression. Instead this is the function call notation in Python.
Also remember that you must initialize all variables before using their values for assignment. Python has no default value for unnamed variables so when you reference the value of sum with sum += expr which is equivalent to sum = sum + expr you are trying to reference a name (sum) that is not binded to any object at all.
The following revision to your function should do the trick. Notice how I place multiplication operators (*) between every expression that you intend to multiply.
def TrapArea(n):
sum = 0
for i in range(1, n):
i = float(i)
deltax = (pi-0)/n
sum += (1.0/2.0)*(((pi-0)/n)*(sin((i-1)/n*(pi-0))) + sin((i/n)*(pi-0)))*deltax
return sum
EDIT: I also dealt with the float division issue by converting i to float(i) within every iteration of the loop. In Python 2.x, if you divide one integer type object with another integer type object, the expression evaluates to an integer regardless of the actual value.
A "nicer" way to do the trapezoid rule with equally-spaced points...
Let dx = pi/n be the width of the interval. Also, let f(i) be sin(i*dx) to shorten some expressions below. Then interval i (in range(1,n)) contributes:
dA = 0.5*dx*( f(i) + f(i-1) )
...to the sum (which is an area, so I'm using dA for "delta area"). Factoring out the 0.5*dx, makes the whole some look like:
A = 0.5*dx * ( (f(0) + f(1)) + (f(1) + f(2)) + .... + (f(n-1) + f(n)) )
Notice that there are two f(1) terms, two f(2) terms, on up to two f(n-1) terms. Combine those to get:
A = 0.5*dx * ( f(0) + 2*f(1) + 2*f(2) + ... + 2*f(n-1) + f(n) )
The 0.5 and 2 factors cancel except in the first and last terms:
A = 0.5*dx(f(0) + f(n)) + dx*(f(1) + f(2) + ... + f(n-1))
Finally, you can factor dx out entirely to do just one multiplication at the end. Converting back to sin() calls, then:
def TrapArea(n):
dx = pi/n
asum = 0.5*(sin(0) + sin(pi)) # this is 0 for this problem, but not others
for i in range(1, n-1):
asum += sin(i*dx)
return sum*dx
That changed "sum" to "asum", or maybe "area" would be better. That's mostly because sum() is a built-in function, which I'll use below the line.
Extra credit: The loop part of the sum can be done in one step with a generator expression and the sum builtin function:
def TrapArea2(n):
dx = pi/n
asum = 0.5*(sin(0) + sin(pi))
asum += sum(sin(i*dx) for i in range(1,n-1))
return asum*dx
Testing both of those:
>>> for n in [1, 10, 100, 1000, 10000]:
print n, TrapArea(n), TrapArea2(n)
1 1.92367069372e-16 1.92367069372e-16
10 1.88644298557 1.88644298557
100 1.99884870579 1.99884870579
1000 1.99998848548 1.99998848548
10000 1.99999988485 1.99999988485
That first line is a "numerical zero", since math.sin(math.pi) evaluates to about 1.2e-16 instead of exactly zero. Draw the single interval from 0 to pi and the endpoints are indeed both 0 (or nearly so.)

Taking square of summed numbers

This code adds all natural numbers up to 10, then takes the square of that sum in Python. Where did I go wrong?
def square_of_sum():
sum = 0
for x in xrange(11):
if x <= 10:
x + sum = sum
x += 1
else:
print sum*sum
break
Ah, I see you like Project Euler :)
Solution
I think this is what you meant by your code:
def square_of_sum():
sum_ = 0
for x in xrange(1, 11):
sum_ += x
return sum_ ** 2
To rewrite this more idiomatically, use generator comprehensions and built-ins:
def square_of_sum():
return sum(range(11)) ** 2
If your performance conscious, you can eliminate the loop by noticing that your finding the sum of an arithmetic series:
def square_of_sum(x):
print (x * (x + 1) / 2) ** 2
Fixes
As to why your code isn't working, it's b'coz of many reasons.
First of all, I think you're confused about how the for loop in Python works. Basically, it just loops over an array. You didn't have to check and break when x became greater than 10, nor increment it. Read up on the Python docs on how to use the for loop. To see an example of when to use it, see the wiki page.
Secondly, variable assignments are done with the variable on the left and the expression to be evaluated on the right. So x + sum = sum should really have been sum = sum + x or sum += x for brevity.
Thirdly, sum is a built-in function. You probably didn't want to nor shouldn't over-shadow it, so rename your sum variable to something else.
And last, sum*sum is equivalent to just raising it to the power of 2 and you can do that using the ** operator as so: sum ** 2.
Hope this helped you understand.
To fix the errors in your code:
def square_of_sum():
s = 0
for x in xrange(11):
s += x
print s**2
or, more idiomatically,
def square_of_sum(n):
print sum(range(n + 1)) ** 2
or, to eliminate the loop:
def square_of_sum(n):
print (n * (n + 1) / 2) ** 2
A couple of problems. First of all, sum is a builtin function, so you probably don't want to name anything that, so use a variable called something like total instead.
Second, variables assignment is done with the variable on the left and the expression on the right, so x + total = total should be total = x + total, or total += x for brevity.
Third, since the case when x == 11 is basically just a return case, it should be outside the loop.
And, finally, total * total is equivalent to total ** 2; this is easier to use for things like
def square_of_sum():
total = 0
for x in xrange(11):
if x <= 10:
total += x
x += 1
print total ** 2
But, if I were you, I'd just use
sum(range(11))**2

Python code translation issue or language difference in random number generation

I'm new to Python and have no experience with QBasic. I was running a simulation in Python that came up with theoretically wrong values. I then ran it in QBasic and came up with theoretically predicted values.
Here are the test cases. I'm only counting the probability P(0.9<%Y<=1.8) so the count has to fall within those values. The 1-random.random() was only for that case, when I tried using that for all the cases they still came up with the wrong values. Here's the theoretical outcomes and you can see how it's different:
y~u(0,1)
= 0.575
y~exp(2)
= 0.3371
x1~u(0,1)
x2~u(0,2)
= 0.4475
P(y=0.25)=0.8
P(y=1.5)=0.2
= 0.32
In Python the simulation code is:
def test():
x1,x2,c = 0.0,0.0,0.0
for i in range(10000):
if random.random()< 0.8:
x1 += 0.25
else:
x2 += 1.5
y = x1 + x2
if y>0.9 and y<=1.8:
c = c + 1
return x1,x2,c
print "test: ",test()
def sim(a,b):
#pyab1 = sum([a for a in a if a>0.9 and a<=1.8])/10000
#pyab2 = sum([b for b in b if b>0.9 and b<=1.8])/10000
#print "*****",float(pyab1+pyab2)
#print a+b
#array1 = [[a],[b]]
array1 = a+b
#array1.extend(a)
#array1.extend(b)
#c = 0
#for y in array1:
#if y>0.9 and y<=1.8:
#c = c + 1
pyab = sum([y for y in array1 if y>0.9 and y<=1.8])/10000
print("P(a < x <= b) : {0:8.4f}".format(pyab))
Here's the Python output followed by the values it's supposed to give, but this shows how far off the results are.
case 1: P(a < x <= b) : 0.7169 #should be 0.575
case 2: P(a < x <= b) : 0.4282 #should be 0.3371
case 3: P(a < x <= b) : 0.5966 #should be 0.4475
case 4: P(a < x <= b) : 0.5595 #should be 0.32
In QBasic the simulation code is:
Case 1:
RANDOMIZE
FOR i = 1 TO 10000
X1 = RND(1)
X2 = RND(1)
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Case 2:
RANDOMIZE
FOR i = 1 TO 10000
X1 = (-0.5)*(LOG(1-RND(1)))
X2 = (-0.5)*(LOG(1-RND(1)))
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Case 3:
RANDOMIZE
FOR i = 1 TO 10000
X1 = RND(1)
X2 = RND(1)*2
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Case 4:
RANDOMIZE
FOR i = 1 TO 10000
X14 = RND(1)
X24 = RND(1)
IF (X14<0.8) THEN X41=0.25 ELSE X41=1.5
IF (X24<0.8) THEN X42=0.25 ELSE X42=1.5
Y = X1+X2
IF (Y>0.9) AND (Y<=1.8) THEN C=C+1
NEXT i
PRINT C/10000
Here's the QBasic output, which shows how this is actually getting the right results.
case 1: P(a < x <= b) : 0.5715
case 2: P(a < x <= b) : 0.3371
case 3: P(a < x <= b) : 0.4413
case 4: P(a < x <= b) : 0.3213
All of the above code works for me without error. I don't see any differences in the algorithm used to get the values. Not sure if Python generates numbers differently from QBasic, and if that accounts for the reason behind this behavior.
I'm new to both of these languages but QBasic seems very primitive, and it would seem more likely that Python would get the right answer and QBasic the wrong one. But the opposite is happening. It doesn't appear to be something related to any difference in code. On translation, they both seem to be saying the same thing.
I'm interested in the reason why they give the 2 different outcomes. I'm more interested in why Python is giving the wrong answers and QBasic is giving the right ones.
Your Python code is totally wrong. What I think you want it to do is the following:
Take two arrays, a and b, each containing 10000 numbers generated by some random function. (Equivalently, each an array of 10000 samples from data following some given distribution.)
Pair up the values into 10000 pairs, with each pair taking an element from a and an element from b.
Take the sum of each pair.
Count how many of those 10000 pair-sums lie between 0.9 and 1.8
Divide the above count by 10000 to get the probability that any given pair of data drawn from these distributions will sum to between 0.9 and 1.8, and print that probability.
However, your sim(a, b) function is doing something wildly different. Basically, what you're actually doing is:
Concatenate the two 10000 element arrays, forming a 20000-element array of their elements.
Take the sum of any elements in this new 20000-element array that are greater than 0.9.
Divide that sum by 10000 and print it.
This algorithm bears no resemblance to anything in your Q-Basic code.
If I've understood your problem properly, I think what you want your sim function to be is this:
def sim(x_sample, y_sample):
count = 0
for i in range(10000):
if 0.9 <= x_sample[i] + y_sample[i] <= 1.8:
count += 1
probability = count/10000.0
print("P(a < x <= b) : {0:8.4f}".format(probability))
(There are almost certainly more elegant and Pythonic ways to implement the above function, but this way should be easy for a Python newbie to understand.)
Here are the results of tests I ran in the interpreter for your cases 1 to 3, as I've understood them from the QBasic program. I haven't included a version of test 4 because I didn't understand the QBasic code for test 4. The results for the first three tests are what you said they should be, though.
>>> from random import random
>>>
>>> sim([random() for i in range(10000)],
... [random() for i in range(10000)])
P(a < x <= b) : 0.5746
>>>
... from math import log
>>>
>>> sim([-0.5*log(1-random()) for i in range(10000)],
... [-0.5*log(1-random()) for i in range(10000)])
P(a < x <= b) : 0.3405
>>>
... sim([random() for i in range(10000)],
... [2*random() for i in range(10000)])
P(a < x <= b) : 0.4479

Categories

Resources