Why is this 'for' loop going too long? - python

Below is a small code snippet that essentially tries to use a for loop to find all multiples of a = 3 that are less than 1000 and sum them together. For some reason the final value of mult is 2997, which is not within range(max). Thus, I'm confused as to why the final value of mult isn't 999 and why the for loop even executes after mult does hit a value of 999.
a = 3
max = 999
sum = 0 #cumulative sum of 'a' multiples
mult = 0 #current multiple of 'a'
k = 1 #counter variable
# --- Find all multiples of 'a' that are below 1000 ---
for mult in range(max):
mult = a*k
print(mult)
sum = sum + mult
k+=1
Also, it seems that changing the value of a directly affects how high mult will get before the loop is finished. More precisely, I've found that the final value of mult is equal to a*max, and I cannot think of a reason why this would be.
Edit: Nevermind, I realized that I intended to use a while loop but made the mistake of implementing a for loop instead. Sorry about this. Thank you to everyone who took the time to answer me.

Unlike other programming languages, for loops in Python strictly iterate over the given values.
I. e., with for mult in range(max):, range() defines what values should be assigned to mult on every loop run. It is not affected by any values you assign to mult later.
This concept is called iteration. range(max) returns an object which gives on every loop run the next value. In Python 2, this was just a list containing these values, in Python 3, it is an object which calculates the values "on demand".

It actually doesn't. It goes for exactly 999 times. The last output however is 2997, but as you can see in the output it always increases by 3. This is due to you reassigning mult to a*k.

Since mult is defined as the product of a (a constant equal 3) and k, initially equal to 1, incremented 998 times by 1 every time the loop passes, i.e. it is 999 at the end and 3 * 999 = 2997 in total.

If I am correct, then you want to print all the values between 0 and max and sum them right?
then the following code should help you.
for mult in range(max+1):
if mult % a == 0:
print mult
sum = sum + mult
print sum

Related

Expanding taylor series using relation between successive terms with a specified tolerance in spyder

I have used this code to determine the value of e^pi with a specified tolerance in spyder(Python 3.8).
from math import*
term=1
sum=0
n=1
while (abs(e**pi-term))>0.0001:
term=term*pi/n
sum+=term
n+=1
print(sum,e**pi)
I ran this code several times. But spyder is not showing anything when I run this code. I am new to python. So now I have no way to know whether this code is correct or not. It would be beneficial if you could verify whether this code is valid or not.
Okay, so the taylor series for e^x is sum n=0 to inf of x^n / n! which you seem to be doing correctly by multiplying by pi/n during each iteration.
One problem I noticed was with your while loop condition. To check the error of the function, you should be subtracting your current sum from the actual value. This is why you enter the infinite loop, because your term will approach 0, so the difference will approach the actual value, e^pi, which is always greater than .0001 or whatever your error is.
Additionally, since we start n at 1, we should also start sum at 1 because the 0th term of the taylor series is already 1. This is why you enter the infinite loop, because
Therefore, our code should look like this:
from math import*
term = 1
sum = 1
n = 1
while (abs(e ** pi - sum)) > 0.0001:
term = term * pi / n
sum += term
n += 1
print(sum,e**pi)
Output:
23.140665453179782 23.140692632779263
I hope this helped answer your question! Please let me know if you need any further clarification or details :)
The while loop does not end.
I have stuck in a condition to break out of the loop (at 10,000 cycles) to show this:
from math import*
term=1
sum=0
n=1
while (abs(e**pi-term))>0.0001:
term=term*pi/n
sum+=term
n+=1
if n > 10_000:
print('large n')
break
print(sum,e**pi)
If you replace the while loop with a for loop, then you can see each iteration as follows.
term=1
sum=0
for n in range(1, 101):
term=term*(math.pi)/n
sum+=term
print(n, sum, math.e**math.pi)
And this is the result for the first 10 items:
1 3.141592653589793 23.140692632779263
2 8.076394854134472 23.140692632779263
3 13.244107634184441 23.140692632779263
4 17.30281976060121 23.140692632779263
5 19.852983800478555 23.140692632779263
6 21.188246569333145 23.140692632779263
7 21.787511098653937 23.140692632779263
8 22.02284172901283 23.140692632779263
9 22.104987615623955 23.140692632779263
10 22.13079450701397 23.140692632779263
The numbers are getting larger, hence the while loop was never exited (and your laptop probably got quite hot in the process !!).

Some for loop number decreasing when they should be increasing while iterating through a list

I've been looking at my code for a while and I'm just stuck as to where I messed up, so maybe one of you can help.
What my for loop should do is: It iterates through a long list of times. It averages the first 100 times, and sets that as a value. This part works. What it should do next is add one to t (the value i'm using for my for loop) so it will average the 1st and 101st time together, if this average is less than .1 seconds faster, add the t value to a list of x values, then, set that value as the new average to beat. If it's not .1 lower, we increase t and try again until it works.
Here's my code, and I'll walk you through what it means to make it more readable
for t in times:
t = int(t)
sumset = sum(times[t:t + 100])
avgset = (int(round(float(sumset/100), 3) * 10)) /10
if t + 100 > len(times):
break
elif (avgset) <= firstavg - .1:
avglist.append(avgset)
firstavg -= .1
xlist.append(t)
print(t)
print("avgset is "+str(avgset))
print("should decrease by .1 " + str(math.ceil(firstavg * 10) / 10))
tlist.append(t)
t += 1
else:
t += 1
I'll explain it here.
for t in times:
t = int(t)
sumset = sum(times[t:t + 100])
avgset = (int(round(float(sumset/100), 3) * 10)) /10
for each value in the my list called times, we take the value and make sure it's an int, I do this because earlier I was getting an indexing problem saying it wasn't an int. Sumset gets the sum of the first 100 times that we need, and avgset turns it into an average, multiplies it by 10, uses int to chop off the decimal, and divide by ten to get a tenth value.
Ex
12.34 * 10 = 123.4, int(123.4) = 123, 123 / 10 is 12.3.
Then here
if t + 100 > len(times):
break
We make sure there are 100 values left to iterate through, if not we end the loop.
On this big chunk
elif (avgset) <= firstavg - .1:
avglist.append(avgset)
firstavg -= .1
xlist.append(t)
print(t)
print("avgset is "+str(avgset))
print("should decrease by .1 " + str(math.ceil(firstavg * 10) / 10))
tlist.append(t)
t += 1
We check: if the set is <= to the first average - .1, we append that set of averages to a list of lowering averages. Then we decrease the first avg, and append the value of t to a list that will make up our x-values. What it should do, is produce me a list of x values where each value corresponds to a decrease of .1 from the original average (t: t +100) where t is 0. And we get a y-list (which would be avglist) which is each decrease of .1. I'm not sure where I messed up, so if someone could point me in the right direction I would really appreciate it, thanks!
In my opinion, there are multiple things to address in your code:
1) The main and most important is that you are mixing up the elements in your list (floats) with their indices, i.e. their positions in the list. What you want is to iterate over the indices, not over the elements themselves. What I mean is that given the list:
my_list = [5.67, 4.23, 7.88, 9.5]
the indices of [5.67, 4.23, 7.88, 9.5] are respectively: 0,1,2,3. Python wants to have integer number to iterate because it interpretes these numbers as the position of the elements in the list, regardless of their value. And positions, obviously, always need to be integers, i.e. you are either the 4th or the 5th, not the 4.23th. However, this DOES NOT mean that the values of the elements themselves need to be integers. For accounting for this difference there is the python builtin function enumerate():
>>> for index, value in enumerate([5.67, 4.23, 7.88, 9.5]):
... print (index, '->', value)
...
0 -> 5.67
1 -> 4.23
2 -> 7.88
3 -> 9.5
>>>
this is the reason for which you needed to convert your values (not the indices) to integers, and make the trick of multiplying and dividing by 10 for not losing the 0.1 resolution that you use to compare. You can forget about all that.
2) You do not really need to check in each iteration wheter there are still 100 elements left in the list or not. It suffices to iterate until the -100th element:
for index, time in enumerate(times[:-100]):
and it will automatically stop at the -100th. However, when you do it, remember you want to use always index as the iterator variable, not time. Moreover, in another for loop you might use in some other case, if you need to check whether some condition is fulfilled to process the current element, and if not skip to the next one, you should use continue instead of break:
for index, time in enumerate(times):
if index+100 > len(times):
continue
continue gets you out of the if statement and brings you to the for loop, ready to iterate with the next element. break will break the for loop and stop the iteration.
3) At the end of each of your iterations you have a
elif (...):
...
t += 1
else:
t += 1
this is wrong in many ways:
3.1) first because you are inside an iterator, and t refers to the variable you are using for iterating. You do not need at all to tell the iterator to sum 1 to the iteration variable at the end of each iteration. Doing that is its one job. It knows.
3.2) Supposing that would be any other control variable inside the loop that you indeed need to manually increase by one, you are repeating code lines. You basically get the same effect if you remove the else clause and remove the indent of the last line of the elif clause:
elif (...):
...
t += 1
so the algorithm will fall into t +=1 regardles of whether the elif clause is satisfied or not.
3.3) This is related to the above bullet 1): In your particular case and since you are wrongly using t to iterate (as discussed above), by doing t += 1 you are modifying the list you iterate over, i.e. you are altering the input data.
Taking all this into account, one possible way to roughly implement your code could be:
import numpy as np
times = 100*np.random.rand(150)
list_of_avgs = [np.sum(times[:100])/100.]
for index, element in enumerate(times[:-100]):
avg = np.sum(times[index:index+100])/100.
if avg + 0.1 <= list_of_avgs[-1]:
list_of_avgs.append(avg)
else:
continue
print (list_of_avgs)
which results into (input data is radomly generated):
[49.779866192794358, 49.594673775689778, 49.4409179407875,
49.304759324340424, 49.106580355542434, 48.651419303532919,
48.505888846346672, 47.834645246733295, 47.300679740055074,
46.956253292222293, 46.598627928361239, 46.427709019922297]
Cheers and good luck!
D.

How to set index as variables?

I am building a function, which contains many loops and conditions in it.
The input of the function is an element of a list.
I want the function to generate the result so that the nex time I don't need to run through those loop. The real code is really large so I pasted the main lines as follows, which is a toy model of the real code:
a=[1,2,3,4,5,6,7]
def ff(x):
b=0
for i in range(10000):
for k in range(10000):
if k/2 >20:
for j in range(1000):
if j**2-j>1:
b += a[x]^2+a[x]
return b
ff(2)
So, in fact the result of ff should be simple, but due to the loops and conditions it runs really slow. I don't want to run through the loops each time I call ff.
A bit more like the idea that the function is a tensor in tensorflow, and index is the feed value. The structure is built first and then can be executed with different feed in values. Maybe what I want is symbolic computation.
Is there a way so that I can store the result as a sturcture and next time I just feed in the value of the index.
I cannot simply feed the values of a, since a can be some other shapes.
Your code is equivalent to (if you'll start analyzing what each one of the loops is actually doing...):
def ff(x):
return 995900780000 * (a[x]^2+a[x])
This code should run very fast...
The condition k/2 >20 can be restated as k > 40; so rather than starting the k-loop from 0, start it from 41 and eliminate that condition. Likewise, the condition j**2 - j > 1 implies that you are only interested in j >= 2 since one solution of that is less than 0 (and you aren't interested in those values and the other is about 1.6 and the first integer greater than that is 2). So start the j loop from 2 and eliminate that condition. Finally, your b value does not depend on i, k or j, so make the rhs 1. You now have
def ff(x):
b=0
for i in range(10000):
for k in range(41, 10000):
for j in range(2, 1000):
b += 1
return b
The j loop will run 1000 - 2 = 998 times; k will run 10000 - 41 = 9959 times and i will run 10000 times. The total number of times that b will be incremented is 998*9959*10000 = 99390820000. That's how many times you will have added your rhs (a[x]**2 + a[x]) together...which, except for a different value, is what #alfasin is pointing out: your loops are effectively adding the rhs 99390820000 times so the result will be 99390820000*(a[x]**2 + a[x]) and now you never have to run the loop. Your whole function reduces to:
def ff(x):
return 99390820000*(a[x]**2 + a[x])
The "structure" of adding something within nested loops is to multiply that something by the product of the number of times each loop is run. So if you had
b = 0
for i in range(6):
for j in range(7):
b += 1
the value of b would be 6*7...and the answer (as always ;-)) is 42. If you were adding f(x) to b each time then the answer would be 42*f(x).

Using python how do I repeatedly divide a number by 2 until it is less than 1.0?

I am unsure of how to create the loop to keep dividing the number by two? Please help. I know you you can divide a number by 2 don't know how to create the loop to keep dividing until it is less than 1.0.
It depends on what exactly you're after as it isn't clear from the question. A function that just divides a number by zero until it is less than 1.0 would look like this:
def dividingBy2(x):
while x > 1.0:
x = x/2
But this serves no purpose other than understanding while loops, as it gives you no information. If you wanted to see how many times you can divide by 2 before a number is less than 1.0, then you could always add a counter:
def dividingBy2Counter(x):
count = 0
while x > 1.0:
x = x/2
count = count + 1
return count
Or if you wanted to see each number as x becomes increasingly small:
def dividingBy2Printer(x):
while x > 1.0:
x = x/2
print(x)
b=[] #initiate a list to store the result of each division
#creating a recursive function replaces the while loop
#this enables the non-technical user to call the function easily
def recursive_func(a=0): #recursive since it will call itself later
if a>=1: #specify the condition that will make the function run again
a = a/2 #perform the desired calculation(s)
recursive_func(a) #function calls itself
b.append(a) #records the result of each division in a list
#this is how the user calls the function as an example
recursive_func(1024)
print (b)

Python Time Complexity (run-time)

def f2(L):
sum = 0
i = 1
while i < len(L):
sum = sum + L[i]
i = i * 2
return sum
Let n be the size of the list L passed to this function. Which of the following most accurately describes how the runtime of this function grow as n grows?
(a) It grows linearly, like n does.
(b) It grows quadratically, like n^2 does.
(c) It grows less than linearly.
(d) It grows more than quadratically.
I don't understand how you figure out the relationship between the runtime of the function and the growth of n. Can someone please explain this to me?
ok, since this is homework:
this is the code:
def f2(L):
sum = 0
i = 1
while i < len(L):
sum = sum + L[i]
i = i * 2
return sum
it is obviously dependant on len(L).
So lets see for each line, what it costs:
sum = 0
i = 1
# [...]
return sum
those are obviously constant time, independant of L.
In the loop we have:
sum = sum + L[i] # time to lookup L[i] (`timelookup(L)`) plus time to add to the sum (obviously constant time)
i = i * 2 # obviously constant time
and how many times is the loop executed?
it's obvously dependant on the size of L.
Lets call that loops(L)
so we got an overall complexity of
loops(L) * (timelookup(L) + const)
Being the nice guy I am, I'll tell you that list lookup is constant in python, so it boils down to
O(loops(L)) (constant factors ignored, as big-O convention implies)
And how often do you loop, based on the len() of L?
(a) as often as there are items in the list (b) quadratically as often as there are items in the list?
(c) less often as there are items in the list (d) more often than (b) ?
I am not a computer science major and I don't claim to have a strong grasp of this kind of theory, but I thought it might be relevant for someone from my perspective to try and contribute an answer.
Your function will always take time to execute, and if it is operating on a list argument of varying length, then the time it takes to run that function will be relative to how many elements are in that list.
Lets assume it takes 1 unit of time to process a list of length == 1. What the question is asking, is the relationship between the size of the list getting bigger vs the increase in time for this function to execute.
This link breaks down some basics of Big O notation: http://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/
If it were O(1) complexity (which is not actually one of your A-D options) then it would mean the complexity never grows regardless of the size of L. Obviously in your example it is doing a while loop dependent on growing a counter i in relation to the length of L. I would focus on the fact that i is being multiplied, to indicate the relationship between how long it will take to get through that while loop vs the length of L. Basically, try to compare how many loops the while loop will need to perform at various values of len(L), and then that will determine your complexity. 1 unit of time can be 1 iteration through the while loop.
Hopefully I have made some form of contribution here, with my own lack of expertise on the subject.
Update
To clarify based on the comment from ch3ka, if you were doing more than what you currently have inside your with loop, then you would also have to consider the added complexity for each loop. But because your list lookup L[i] is constant complexity, as is the math that follows it, we can ignore those in terms of the complexity.
Here's a quick-and-dirty way to find out:
import matplotlib.pyplot as plt
def f2(L):
sum = 0
i = 1
times = 0
while i < len(L):
sum = sum + L[i]
i = i * 2
times += 1 # track how many times the loop gets called
return times
def main():
i = range(1200)
f_i = [f2([1]*n) for n in i]
plt.plot(i, f_i)
if __name__=="__main__":
main()
... which results in
Horizontal axis is size of L, vertical axis is how many times the function loops; big-O should be pretty obvious from this.
Consider what happens with an input of length n=10. Now consider what happens if the input size is doubled to 20. Will the runtime double as well? Then it's linear. If the runtime grows by factor 4, then it's quadratic. Etc.
When you look at the function, you have to determine how the size of the list will affect the number of loops that will occur.
In your specific situation, lets increment n and see how many times the while loop will run.
n = 0, loop = 0 times
n = 1, loop = 1 time
n = 2, loop = 1 time
n = 3, loop = 2 times
n = 4, loop = 2 times
See the pattern? Now answer your question, does it:
(a) It grows linearly, like n does. (b) It grows quadratically, like n^2 does.
(c) It grows less than linearly. (d) It grows more than quadratically.
Checkout Hugh's answer for an empirical result :)
it's O(log(len(L))), as list lookup is a constant time operation, independant of the size of the list.

Categories