loop through numbers and evaluate if numbers are divisible by certain numbers - python

i wrote a program to evaluate what numbers in a certain range are divisble by only certain numbers (in the range 1 to 9). So far the code seems to work but i tested the steps it takes through at pythontutor http://www.pythontutor.com/visualize.html#mode=edit and something odd happened.
In the second loop, the code doesnt always check all values (k) for its divisibility but sometimes leaves out the last value (k).
Better to give you an example:
Liste = []
for i in range (1500, 1700):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Teiler = [2, 3, 4, 6, 8, 9]
for k in Liste:
z = 0
for w in range (len(Teiler)):
if k%Teiler[z] == 0:
Liste.remove(k)
z += 1
break
else:
z += 1
print (Liste)
Here the output is as follows:
[1505, 1540, 1575, 1610, 1645, 1680]
[1505, 1575, 1645]
And its the output that its supposed to be, as in the second list are only the numbers divisible by 5 and 7. Also on pythontutor the second for loop goes through all values (k).
But when i change the range of number like the following (line 2, change range from 1700 to 1800):
Liste = []
for i in range (1500, 1800):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Teiler = [2, 3, 4, 6, 8, 9]
for k in Liste:
z = 0
for w in range (len(Teiler)):
if k%Teiler[z] == 0:
Liste.remove(k)
z += 1
break
else:
z += 1
print (Liste)
the output is as the following:
[1505, 1540, 1575, 1610, 1645, 1680, 1715, 1750, 1785]
[1505, 1575, 1645, 1715, 1785]
If i run this code on pythontutor, the code stops at k = 1750 and doesnt check for k = 1785.
Why is it behaving that way?
Is it a problem with the check on pythontutor?
Or is there a problem with my code?
I want to understand why Python is doing what it does.
Thank you very much for your help. Iam sorry if this is a noob question or iam missing out something obvious.

Your problem is in this line:
Liste.remove(k)
because you are removing element from the same list that you use for the for loop so when you delete an element the list is going to be shorter but next iteration you will jump an element.
I advice to use two list or use a while loop and when you remove the element from list you don't go to next iteration and you decrease the len of the list of 1; example:
Liste = []
for i in range (1500, 1800):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Teiler = [2, 3, 4, 6, 8, 9]
l=len(Liste)
i=0
while i<l:
k=Liste[i]
z = 0
for w in range (len(Teiler)):
if k%Teiler[z] == 0:
Liste.remove(k)
l-=1
i-=1
z += 1
break
else:
z += 1
i+=1
This code could be improved but was for let you understand how you can do it

Don't remove items from the list you iterate over, as the other answer states. Changing the length of the list affects the iteration. Example:
>>> L=list(range(10))
>>> for i in L:
... if i==5: L.remove(i)
... print(i)
...
0
1
2
3
4
5 # removed 5, and skipped 6!
7
8
9
Also w is never used, although z is the equivalent. Instead, iterate directly over Teiler and add items to a new list if it satisfies your condition:
Liste = []
for i in range (1500, 1800):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Liste2 = []
Teiler = [2, 3, 4, 6, 8, 9]
for k in Liste:
for w in Teiler:
if k % w == 0:
break
else:
Liste2.append(k)
print(Liste2)
Output:
[1505, 1645, 1715]
If you haven't seen for/else, the else only executes if you don't break from the for loop, so all of k % w != 0 has to be true.
Another option is to use list comprehensions, which really simplify the code:
L = [i for i in range(1500,1800) if i%5 == 0 and i%7 == 0]
L = [x for x in L if all(x % k != 0 for k in (2,3,4,6,8,9))]
print(L)
Output:
[1505, 1645, 1715]
Note 1575 and 1785 are divisible by 3, so both your sample lists had errors from removing values in the list.

Related

How to break while loop

I am trying to find if a element in a list exists in a range where the list size is known and number of ranges is also known
I have tried a logic in python using a while loop
a[1]=[23,330,460]
r=[1,120,300,450,600]
and my range list are
1 to 100 for i=0
120 to 220 for i=1
300 to 400 for i=2
450 to 550 for i=3
600 t0 700 for i=4
a[1] has some values which is compared with the elements in range
a[0] is output got after checking
if a[1] is not in the range defined
a[0] must be 0,
else 1
j=0
for i in range(0,5):
while j<3:
l=list(range(r[i],r[i]+100))
if a[1][j] in l:
a[0].append(1)
j=j+1
else:
a[0].append(0)
j=j
I expect the output to be a[0]=[1 0 1 1 0]
Your code does not change 'j' value in else since it executes first in this case,so it becomes infinite.
a=[[],[23,330,460]]
r=[1,120,300,450,600]
for i in range(5):
j=0
l=list(range(r[i],r[i]+100))
while j<3:
if a[1][j] in l:
a[0].append(1)
break
j=j+1
else:
a[0].append(0)
print(a[0])
#[1, 0, 1, 1, 0]
Using for
a=[[],[23,330,460]]
r=[1,120,300,450,600]
for i in range(5):
l=list(range(r[i],r[i]+100))
for j in a[1]:
if j in l:
a[0].append(1)
break
else:
a[0].append(0)
print(a[0])
#[1, 0, 1, 1, 0]
Using any
Updated as per tripleee answer, range is not necessary
a=[[],[23,330,460]]
r=[1,120,300,450,600]
for i in range(5):
a[0].append(int(any(r[i]<=j<=r[i]+100 for j in a[1])))
print(a[0])
#[1, 0, 1, 1, 0]
What about if you use another if: if the result is what you expect, keep going. If not, break.
result = []
for pos, val in enumerate(a[1]):
if r[pos] <= val <= r[pos]+100:
comp = 1
else:
if comp == 1 then
echo 'Well done'
else break 0;
result.append(comp)
Not pretty sure btw...
Creating a list of 100 elements on each iteration over the loop is extremely inefficient and unidiomatic. I think you want simply
result = []
for pos, val in enumerate(a[1]):
if r[pos] <= val <= r[pos]+100:
comp = 1
else:
comp = 0
result.append(comp)
or more succinctly with a list comprehension
result = [1 if r[pos] <= val <= r[pos]+100 else 0
for pos, val in enumerate(a[1])]
If you want the value to be in a[0] then simply assign a[0] = result at the end; but really, this design is extremely dubious. I would suggest you keep two separate variables, or possibly refactor the input and output into a class if you need to keep them together.

Converting a nested for loop to a nested while loop

my_data = [4, 8, 15, 16, 23, 42]
index = 0
for item in my_data:
k = 0
for i in range(2, item+1):
if (i % 2 == 0):
k += 1
my_data[index] = k
index += 1
print(my_data)
The above code takes all the elements of the given list and divides them by 2, and it works all fine, however I was trying to convert it into while loop for sake of practice,but for some reason I am getting no error, but the code keeps running and I had to force stop it.
i = 0
while i < len(my_data):
j = 2
while j < i+1:
if j % 2 == 0:
j += 1
my_data[i] = j
i += 1
print my_data
And here is the method I have tried to implement which seems to not work as far as I am concerned. If someone could clarify it for me I would appreciate it. Thank you in advance.
your second while loop moves forward only if j is pair and if j is not (since j increase only by 1) the while loop will loop for ever.
you've made a little mistake by replacing k with j, and you should continue using k
i = 0
while i < len(my_data):
j = 2
k = 0
while j < i+1:
if j % 2 == 0:
k+=1
j += 1
my_data[i] = k
i += 1
print my_data
At line 5:
if j % 2 == 0:
When i==3 and j==3, the if statement is ignored. j can't be increased. It will be looped forever, since the stop criterion is j<i+1
Just add an else statement to handle that case
If you are just trying to divide each item of a list using while you also try this way.
my_data = [4, 8, 15, 16, 23, 42]
index = 0
while len(my_data) > index:
my_data[index] = int(my_data[index]/2)
index +=1
print(my_data)

IndexError: list index out of range when using variabel to indexing

def adjacentElementsProduct(inputArray):
i = 0
n = []
t = int(0)
b = int(1)
while i < len(inputArray):
noob = inputArray[t] * inputArray[b]
t += 1
b += 1
i += 1
n.append(noob)
return n
print(adjacentElementsProduct([3, 6, -2, -5, 7, 3]))
Can some one help me when I try to execute this its return
IndexError: list index out of range
Sorry for my bad English Grammar
You need to change your loop so that i only iterates up to len(inputArray) - 1.
Like this:
def adjacentElementsProduct(inputArray):
i = 0
n = []
t = 0
b = 1
while i < len(inputArray) - 1:
noob = inputArray[t] * inputArray[b]
t += 1
b += 1
i += 1
n.append(noob)
return n
print(adjacentElementsProduct([3, 6, -2, -5, 7, 3]))
This is because your noob array is essentially just each adjacent pair of elements in inputArray. Naturally, the number of pairs in a list of i elements is i - 1. This means that you need to change your loop to stop one iteration before, as it's throwing the IndexError when t is the len(inputArray) - 1 and b is the len(inputArray) (b isn't a valid value in the last iteration of your current code as there's not that many elements).
Here you set i=0, t=0, and b=1, thus your b always greater than i by 1. So, in the last loop (where t and i should equal len(inputArray) -1), the value of b should be len(inputArray) which is bigger than the length of your array.
I am not very sure about your excepted output, but I guess you could fix it like following:
while i < len(inputArray) - 1:
noob = inputArray[t] * inputArray[b]
t += 1
b += 1
i += 1
n.append(noob)
You can change the while loop for a for loop and use try / except to return the result when it reaches the end of the iteration. You can also add in extra except clauses to catch other errors and make use of else and finally if needs be .. https://docs.python.org/3/tutorial/errors.html
def adjacentElementsProduct(inputArray):
i = 0
n = []
t = int(0)
b = int(1)
for i in range(len(inputArray)):
try:
noob = inputArray[t] * inputArray[b]
t += 1
b += 1
i += 1
n.append(noob)
except IndexError:
return n
return n
print(adjacentElementsProduct([3, 6, -2, -5, 7, 3]))
l=[3, 6, -2, -5, 7, 3]
print([x*y for x,y in zip(l,l[1:])])
Here zip is used to create the adjacency pair

Python: parsing a string of concatenated ascending integers

The objective is to parse the output of an ill-behaving program which concatenates a list of numbers, e.g., 3, 4, 5, into a string "345", without any non-number separating the numbers. I also know that the list is sorted in ascending order.
I came up with the following solution which reconstructs the list from a string:
a = '3456781015203040'
numlist = []
numlist.append(int(a[0]))
i = 1
while True:
j = 1
while True:
if int(a[i:i+j]) <= numlist[-1]:
j = j + 1
else:
numlist.append(int(a[i:i+j]))
i = i + j
break
if i >= len(a):
break
This works, but I have a feeling that the solution reflects too much the fact that I have been trained in Pascal, decades ago. Is there a better or more pythonic way to do it?
I am aware that the problem is ill-posed, i.e., I could start with '34' as the initial element and get a different solution (or possibly end up with remaining trailing numeral characters which don't form the next element of the list).
This finds solutions for all possible initial number lengths:
a = '3456781015203040'
def numbers(a,n):
current_num, i = 0, 0
while True:
while i+n <= len(a) and int(a[i:i+n]) <= current_num:
n += 1
if i+n <= len(a):
current_num = int(a[i:i+n])
yield current_num
i += n
else:
return
for n in range(1,len(a)):
l = list(numbers(a,n))
# print only solutions that use up all digits of a
if ''.join(map(str,l)) == a:
print(l)
[3, 4, 5, 6, 7, 8, 10, 15, 20, 30, 40]
[34, 56, 78, 101, 520, 3040]
[34567, 81015, 203040]
little modification which allows to parse "7000000000001" data and give the best output (max list size)
a = 30000001
def numbers(a,n):
current_num, i = 0, 0
while True:
while i+n <= len(a) and int(a[i:i+n]) <= current_num:n += 1
if i+2*n>len(a):current_num = int(a[i:]);yield current_num; return
elif i+n <= len(a):current_num = int(a[i:i+n]);yield current_num;i += n
else: return
print(current_num)
for n in range(1,len(a)):
l = list(numbers(a,n))
if "".join(map(str,l)) == a:print (l)

How do I loop through two lists only iterating one at a time?

Im trying to implement merge sort in python and I'd like to know what is the pythonic way of looping through two lists iterating only one of the lists each loop.
This is what I have now (using indexes)
def merge(array1, array2):
final = []
i = 0
j = 0
while i < len(array1) or j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
elif array2[j] < array1[i]:
final.append(array2[j])
j += 1
# Finished one of the arrays
if i == len(array1):
final.extend(array2[j:])
break
elif j == len(array2):
final.extend(array1[i:])
break
return final
Thanks.
I'm not sure not sure your problem here is using indices, but there's a lot of unneeded code.
Firstly, we can change your initialisers to:
final, i, j = [], 0, 0
Next, if we change your while condition we can remove the breaks:
while i < len(array1) and j < len(array2):
Your elif isn't meaningful as it will always be true, we can thus make your if:
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
And now because we're automatically breaking out of the loop we don't need the breaks and can move the extends outside:
def merge(array1, array2):
final, i, j = [], 0, 0
while i < len(array1) and j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
final.extend(array1[i:])
final.extend(array2[j:])
return final
This gives you smaller, more readable code without actually changing the way you're doing anything, that way you'll still understand it.
Note that we can perform:
final.extend(array1[i:])
final.extend(array2[j:])
outside the loop because one array will have contents, for example [7,9], while the other will be empty([]):
>>> final = [3,6]
>>> array1 = [3,6]
>>> array2 = [7,9]
>>> i = 2
>>> j = 0
>>> array1[i:]
[]
>>> array2[j:]
[7, 9]
>>> final.extend(array1[i:])
>>> final
[3, 6]
>>> final.extend(array2[j:])
>>> final
[3, 6, 7, 9]

Categories