While loop not giving desired outcome - python

New to python here, so not sure if I'm posting in the right place but I was wondering why the following loop does not give an answer. I am trying to add all of the numbers previous to i, (1 + 2 + 3 + ... + i), but the loop I came up with does not complete.
j = 0
i = 17
while i > 0:
j = j + i
i - 1
print(j)
I expect j = 153, but the loop doesn't put out anything.

The issue is with i--you're not properly reducing it by 1, so this is an infinite loop. Try this:
j = 0
i = 17
while i > 0:
j = j + i
i -= 1
print(j)

Notice that your line in the while statement,
i-1, does not assign (= operator) a value to i. i stays 17 forever and ever, and so you see no output because the program gets stuck in your while loop. Try to fix your i assignment, like so:
j = 0
i = 17
while i > 0:
j = j + i
i = i - 1 # <= i = i - 1
print(j)
One way to debug your loop in the future is to put a print statement in the middle, like in the code below. The extra print statements slow your program down a bit, but you'll be able to easily see that your loop is going way more than you want!
j = 0
i = 17
while i > 0:
j = j + i
i = i - 1
print(j)
print(i) # so that we can see that i is going down :P
print(j)
Good luck :)

As noted above, you're not re-assigning to your i within the loop. But I'd also note that it's generally a bad idea to modify an iterator within the iteration loop. You can achieve the expected results in a more pythonic way using a range expression, and wrap it in a function also :)
def foo(max_num):
j = 0
for i in range(max_num+1):
j += i
return j
But even better, without the loop:
def foo(max_num):
return sum(range(max_num+1))

There are a couple of ways to do what you want to do. The way I recommend you do it is as shown below.
total = 0
i = 18
for j in range(i):
total += j
print(total)
The range(i) takes a maximum number i, and allows you to iterate through every number starting from 0 to i-1 (e.g. i = 5 will add numbers 0,1,2,3,4), therefore always make i one more than you need it to be if you want to be if you want to include the max number.

As already stated i - 1 wasn't assigned back to i
Following is a pythonic way of implementing the loop with a list comprehension
Code:
j = 0
i = 17
j = sum([j + x for x in range(i, 0, -1)])
print(j)
>>> 153
As a Function:
def sum_of_steps(start: int, stop: int, step: int) -> int:
return sum([x for x in range(start, stop, step)])
j = sum_of_steps(17, 0, -1)
print(j)
>>> 153
The function, sum_of_steps uses type hints or function annotations.
Python Basics: List Comprehensions

Related

How can I manually progress an iterator variable in Python 3?

I would like to know how to manually progress an iterator variable in Python3. In C, for example, if during a for loop such as:
for( int i = 0, k = 10; i < k; ++i), I could skip loop iterations when i == {2,3,4} by simply setting i from within the loop body like this: if(i == 1) i = 5;
However, when I do something like this from within a for in loop in Python3, the for in loop actually enforces that I cannot do this - it mutates i and sets it to the next sequential number regardless of what I did to the iterator variable in the loop body.
For example in the Python3 code below, I am trying to progress i to the position of j such that I can skip i to the end of the group of spaces once the algorithm has detected a group of spaces:
testString = "This is a testzstring"
# find length and end of each space group
spacePositionsAndLengths = {}
j = 0
length = len(testString)
for i in range(length):
if testString[i] == " ":
j = i # mark beginning of spaces
while j < length and testString[j] == " ":
j += 1
# j-1 is now the last space. Alternatively, j is at first non-space
lengthOfSpaces = j-i
print(f"i: {i}\t lengthOfSpaces: {lengthOfSpaces}")
spacePositionsAndLengths[i] = lengthOfSpaces
i = j # remember, at this point j is at first non-space
print(testString)
That algorithm prints this output when run with:
i: 4 lengthOfSpaces: 1
i: 7 lengthOfSpaces: 1
i: 9 lengthOfSpaces: 3
i: 10 lengthOfSpaces: 2
i: 11 lengthOfSpaces: 1
This is a testzstring
This is decent, but what I want is it to print this:
i: 4 lengthOfSpaces: 1
i: 7 lengthOfSpaces: 1
i: 9 lengthOfSpaces: 3
This is a testzstring
I do not want the redundant 3..2..1 "countdown" style space counting.
Note: I am not building a production application; I am working on algorithms and the instructions require me to NOT use many of the builtin string methods. I note this because I expect comments saying "Why don't you just use X, Y, or Z builtin function and be done with this whole thing in 1 line??"
To manually progress the iterator, you need access to it. You can explicitly create the iterator yourself and then both give it to the for statement and treat it yourself as you wish. Example:
irange = iter(range(10))
for i in irange:
print(i)
next(irange)
Output:
0
2
4
6
8
Though in your example, a while loop might be better.
What you need is a while loop so that you can have your own iterator that you can mutate. It may look something like.
j = 0
i = 0
length = len(testString)
while i < length:
if testString[i] == " ":
j = i # mark beginning of spaces
while j < length and testString[j] == " ":
j += 1
# j-1 is now the last space. Alternatively, j is at first non-space
lengthOfSpaces = j-i
print(f"i: {i}\t lengthOfSpaces: {lengthOfSpaces}")
spacePositionsAndLengths[i] = lengthOfSpaces
i = j # remember, at this point j is at first non-space
i += 1

traversing through a list using recursion

So I am new to recursion and I am trying to make a program where you can enter a list and python tests each integer (lets say 9 for example) and sees if the integer following it is doubled. So if I entered a list of 2 4 8 16 32, would return 4, and -5 -10 0 6 12 9 36, would return 2 because -5 followed by -10 is one and 6 followed by 12 is the second. This is the code I have so far. I feel like I am very close. but just a few thing stand in my way. Any help would be great!
L = []
def countDouble(L):
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
print(y[1])
print(y[0])
count = 0
y[0] += y[0]
# unsure of how to multiple y[0] by 2
if y[0]*2 == y[1]:
count += 1
else:
count += 0
#how would I traverse through the rest of the entered list using recursion?
print(count)
countDouble(L)
If you want/need to solve it using recursion, the following will do the trick:
def count_sequential_doubles(li, count=0):
return count_sequential_doubles(li[1:], count + int(li[0] * 2 == li[1])) if len(li) > 1 else count
I would suggest this recursive way:
def countDouble(L):
count = 0
if len(L) == 1:
return count
else:
if int(L[0])*2 == int(L[1]):
count += 1
return count + countDouble(L[1:])
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = countDouble(y)
print(count)
I urge you to read the entire answer, but in case you are not interested in tips, notes and the process of finding the solution, here are two solutions:
solution using recursion (not recommended):
x = input()
y = x.split(' ')
count = 0
def countDouble(i):
if(i+1 == len(y)):
return 'recursion ends here when'
if(int(y[i])*2==int(y[i+1])):
count += 1
countDouble(i+1)
countDouble(0)
print(count)
this solution just imitates a while loop:
solution using a while loop (recommended):
x = input()
y = x.split(' ')
count = 0
i = 0
while(i < len(y) - 1):
if(int(y[i]) * 2 == int(y[i+1])):
count += 1
i += 1
print(count)
Before I continue, here are a few tips and notes: (some of them will only make sense after)
I assume the 14 in your example is a typo
I didn't put the code in a function because it's not needed, but you can change it easily.
In your code, you are passing L as a parameter to the countDouble() function, but you don't use it. if you don't need a parameter don't pass it.
when splitting the input, the values of the list are still strings. so you have to invert them to integers (for instance, you can do that with the int() 'function') before comparing their values - otherwise multiplying by 2 will just repeat the string. for example: '13'*2 is the string '1313'
I don't know why you why you added y[0] to itself in line 9, but based on the code that comes after this would yield incorrect results, you don't need to change the elements in order to get their value multiplied by 2.
notice that in the else block, nothing has changed. adding 0 to the count doesn't change it. so you can remove the else block entirely
While it's possible to solve the problem in recursion, there's something else designed for these kind of problems: loops.
The problem is essentially repeating a simple check for every element of a list.
This is how I would arrive to a solution
so we want to run the following 'code':
if(y[0]*2 == y[1]):
count += 1
if(y[1]*2 == y[2]):
count += 1
if(y[2]*2 == y[3]):
count += 1
...
of course the computer doesn't understand what "..." means, but it gives us an idea to the pattern in the code. now we can do the following:
divide the extended 'code' into similar sections.
identify the variables in the pattern - the values that change between sections
find the starting values of all variables
find a pattern in the changes of each variable
find a breaking point, a condition on one of the variables that tells us we have reached the last repeating section.
here are the steps in this specific problem:
the sections are the if statements
the variables are the indexes of the elements in y we compare
the first index starts at 0 and the second at 1
both indexes increase by one after each if-statement
when the second index is bigger then the last index of y then we already checked all the elements and we can stop
so all is left is to set the needed variables, have a while loop with the breaking condition we found, and in the while loop have the general case of the repeating sections and then the changing of the variables.
so:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
# setting the starting values of the variables
index1 = 0
index2 = 1
# creating a loop with the breaking condition
while(index2 < len(y)):
# the general case of the repeated code:
if(int(y[index1]) * 2 == int(y[index2])):
count += 1
# changing the variables for the next loop
index1 += 1
index2 += 1
print(count)
We see that the index2 is just index1 + 1 at all time. so we can replace it like that:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
index1 = 0
while(index1 + 1 < len(y)):
if(int(y[index1]) * 2 == int(y[index1 + 1])):
count += 1
index1 += 1
print(count)
Note: You can use a for loop similarly to the while loop
So in summary, you can use recursion to solve the problem, but the recursion would just be imitating the process of a loop:
in each call, the breaking condition will be checked, the repeated code would run and the variables/parameters would change.
Hope you find this answer useful :)
Final edit: OP edited his example so my other code didnt apply
Some good questions people are asking, but in the spirit of helping, here's a recursive function that returns the count of all doubles.
def get_doubles_count_with_recursion(a_list, count, previous=None):
while a_list:
try:
first = previous if previous else a_list.pop(0)
next_item = a_list.pop(0)
except IndexError:
return count
if next_item / 2 == first:
count += 1
return get_doubles_count_with_recursion(a_list, count, next_item)
return count
a_list = [1, 3, 5, 10, 11, 14, 28, 56, 88, 116, 232, 464, 500]
doubles = get_doubles_count_with_recursion(a_list, 0)
print(doubles == 5)
Probably could clean it up a bit, but it's a lot easier to read than the other guy's ;)
If I'm reading your question right, you want a count of all pairs where the 2nd item is double the first. (and the 14 in the first list is a typo). In which case a simple function like this should do the job:
#a = [2,4,8,16,32]
a = [-5, -10, 0, 16, 32]
count = 0
for i, x in enumerate(a):
# Stop before the list overflows
if i < len(a) - 1:
# If the next element is double the current one, increment the counter
if a[i+1] == x * 2:
count = count + 1
else:
break
print(count)

While loop vs a for loop in finding sum of multiples of 3 and 5 under 1000

not really familiar with while loops but I thought it was an alternative so this may be an elementary mistake.
I need to look for the sum of natural numbers under 1000 that are multiples of 3 and 5. E.g for under 10
multiples of 3 and 5 < 10 = 3,5,6,9
sum = 23
My code using a for loop works and is as follows (this was my initial solution):
def multiple():
lst = []
for x in range(334): #didn't know how else to use a for loop but to find the largest value of x using a calculator
if 3*x < limit:
lst.append(3*x)
if 5*x< 1000:
lst.append(5*x)
if (3*x > 1000) and (5*x > 1000): #unnecessary in a forloop with a range but this is just to maintain symmetry with while loop
break
lst2 = list(set(lst)) #remove duplicates
print(sum(lst2))
multiple()
My code using a while loop(this solution doesn't even come out in te console --> maybe this is were the error lies):
def multiple():
lst = []
while True:
x = 1
if 3*x < 1000:
lst.append(3*x)
if 5*x< 1000:
lst.append(5*x)
if (3*x > 1000) and (5*x > 1000):
break
x += 1
lst2 = list(set(lst)) #remove duplicates
print(sum(lst2))
multiple()
Desired output:
233168
In addition to how to rectify the while loop, any improvements on my for loop or while loop would also be welcome. Thanks
Critically Debugging
Since you're new, let's take this opportunity to analyze the bug before we solve it. Note first that you did not notice any printouts at all. Therefore, your print() statement was either not running, or was printing only spaces. We can rule out the latter since sum() will return an integer.
Therefore, the print() is never run. The function is defined and called correctly, so that's not the issue. Now notice while True:; this is an early warning sign. If the while loop never ends, the print() will never run. We do notice there are multiple break statements that should stop the loop, but it's likely there's an issue with them.
Now we check how the loop updates. First, note the i+=1. That seems right. However, i=1 is also within the while loop. This cannot be correct, since every iteration i will be reset. This would cause the loop to run forever.
This type of critical analysis of code is only built through practice, but hopefully this answer offers some insight into how you could have fixed this issue yourself (and how I looked through your code).
Also note that adding print statements into the while loop to test would have allowed you to notice that i was always 1.
Working Code
def multiple():
lst = []
x = 1 # moved from line below
while True:
# x = 1 should not go here
if 3*x < 1000:
lst.append(3*x)
if 5*x< 1000:
lst.append(5*x)
if (3*x > 1000) and (5*x > 1000):
break
x += 1
lst2 = list(set(lst)) #remove duplicates
print(sum(lst2))
multiple()
A fairly straight forward approach is to iterate over every number from 1 to 1000 and check if it is divisible by 3 or 5, and then sum them all up.
total = sum(x for x in range(1, 1001) if x%3 == 0 or x%5 == 0)
total
# returns:
234168
Another solution:
Using for loop:
def multiple():
sum = 0
for _ in xrange(1, 1001):
if _ % 3 == 0 or _ % 5 == 0:
sum += _
return sum
print(multiple())
Using while loop:
def multiple():
sum = 0
cnt = 1
while cnt <= 1000:
if cnt % 3 == 0 or cnt % 5 == 0:
sum += cnt
cnt += 1
return sum
print(multiple())
output:
234168
#!/usr/bin/env python
def multiple():
lst = []
x=1
while (3*x < 1000) or (5*x < 1000):
if 3*x < 1000:
lst.append(3*x)
if 5*x < 1000:
lst.append(5*x)
x += 1
lst2 = list(set(lst)) #remove duplicates
print(sum(lst2))
multiple()

Reverse an array between given indexes

I have been asked an interview question to reverse an array. I used the following method and it worked as expected:
def reverse(array, i, j):
if i > j: # ensure i <= j
i, j = j, i
while i < j:
array[i], array[j] = array[j], array[i]
i += 1
j -= 1
Now, the interviewer asked me to replace the above while loop by for and I was really confused. Please can someone help me here. Thanks in advance.
This is not a for, but you can also do this:
If you follow the python logic where [i:j] means i <= x < j then you can do this:
array[i:j] = array[j-1:i-1:-1]
Other way, if you want to get the elements where i <= x <= j then you can do as #darksky suggested:
array[i:j+1] = array[j: i - 1: -1]
You could forget the for loop entirely and do something like this:
def reverse(array, i, j):
# Make sure i is less than j
if i > j:
i, j = j, i
# Reverse array[i:j+1]
section = array[i:j+1]
section.reverse()
array[i:j+1] = section
(The j+1 is to keep consistent with your function's behaviour; both i and j are treated as inclusive, but Python wants [inclusive, exclusive).)
Or if you want to keep the for loop in and avoid standard functions, then you could do something like this:
def reverse(array, i, j):
# Make sure i is less than j
if i > j:
i, j = j, i
# Reverse array[i:j]
count = j - i + 1
for offset in range(0, count // 2):
first = i + offset
second = j - offset
array[first], array[second] = array[second], array[first]
Other answers here perform this in a really smart way, but a for loop should look something like:
for x in range(j - i):
array[i+x], array[j-x] = array[j-x], array[i+x]
I think more readable example would be:
arr[i,j] = reversed(arr[i, j])

How to convert the following Matlab loop into Python?

I have been working on translating Matlab code into Python and came across a loop that I'm having some difficulty converting as I'm fairly new to both the languages.
if fdp >=2
degreeTwoVector=[];
counter =1;
for i = 1:numVariables
for j = 1:numVariables
degreeTwoVector(counter,:) = [i j 0];
counter = counter +1;
end
end
sortedDegreeTwoVector = sort(degreeTwoVector,2);
degreeTwoVector = unique(sortedDegreeTwoVector, 'rows');
combinationVector = [combinationVector; degreeTwoVector];
end
Here's what I could come up with while converting it to python(incomplete):
if fdp >= 2:
degreeTwoVector = np.array([])
counter = 1
for i in range(1, numVariables+1):
for j in range(1, numVariables+1):
degreeTwoVector(counter, :) = np.array([i, j, 0])
counter = counter + 1
break
sortedDegreeTwoVector = degreeTwoVector[np.argsort(degreeTwoVector[:, 1])]
I certainly know there are some mistakes in it. So I'd be grateful if you could help me complete the conversion and correct any mistakes. Thanks in advance!
You are not too far off:
You do not need a break statement, it is causing a precocious, well, break to the loop (at the first iteration).
So here you go:
numVariables = np.shape(X)[0] # number of rows in X which is given
if fdp >= 2:
degreeTwoVector = np.zeros((numVariables, 3)) # you need to initialize the shape
counter = 0 # first index is 0
for i in range(numVariables):
for j in range(numVariables):
degreeTwoVector[counter, :] = np.array([i, j, 0])
counter = counter + 1 # counter += 1 is more pythonic
sortedDegreeTwoVector = np.sort(degreeTwoVector, axis=1);
degreeTwoVector = np.vstack({tuple(row) for row in sortedDegreeTwoVector})
combinationVector = np.vstack((combinationVector, degreeTwoVector))
EDIT: added equivalent of code outside the loop in the original question.
Apart from the fact that i don't see where you defined combinationVector, everything should be okay.

Categories