Python: program with nested loops not working - python

This program needs to find all permutations of a list by swapping elements using this rule - swap last element until it becomes first (for example 1, 2, 3, 4 becomes 1, 2, 4, 3 and so on until 4, 1, 2, 3), when it becomes the first element then you need to switch last 2 elements and do the same thing in opposite direction (swap first element until it becomes last and then swap first 2 elements and repeat), this is also known as Steinhaus - Johnson - Trotter algorithm.For some reason my implementation isn't working in Python and I'd like to know why and what do I need to do to make it work.
EDIT: By "not working" I mean that the program only prints list1 and does nothing else, the program can only be closed by "killing" it which means that it is stuck in infinite loop (this can be proven by printing all_permutations after appending list1 to all_permutations).
list1 = [0, 1, 2, 3] #list that will be swapped
x = 3 #this is used for swapping
all_permutations = [] #list where permutations will be added
print(list1) #print list1 because it is the first permutation
while len(all_permutations) != 23: #loop until all permutations are found (4! = 24 but since list1 is already 1 permutation we only need 23)
x -= 1
list1[x], list1[x+1] = list1[x+1], list1[x]
all_permutations.append(list1)
#code above swaps the last element until it becomes 1st in the list
if x == 0: #if last element becomes 1st
list1[2], list1[3] = list1[3], list1[2] #swap last 2 elements
while x != 3: #loop which swaps 1st element until it becomes the last element
if len(all_permutations) == 23:
break
else:
continue
x += 1
list1[x-1], list1[x] = list1[x], list1[x-1]
all_permutations.append(list1)
list1[0], list1[1] = list1[1], list1[0] #when loop is over (when 1st element becomes last) switch first 2 elements
all_permutations.append(list1)
else:
continue
print(all_permutations) #print all permutations

while x != 3:
if len(all_permutations) == 23:
break
else:
continue
this piece of code right here will result in an infinite loop. If the length of all_permutations is not 23 it will hit the continue statement. This will send the program back to the beginning of the loop without modifying x or all_permutations.
I believe what you are looking for here is pass which does nothing. continue will move back to the beginning of the loop. So to fix this part of your program you can actually just get rid of the else altogether since the break will exit the loop anyway there is no need for it.
while x != 3:
if len(all_permutations) == 23:
break
x += 1
list1[x-1], list1[x] = list1[x], list1[x-1]
all_permutations.append(list1)
Or you could eliminate the if altogether:
while x != 3 or len(all_permutations) != 23:
x += 1
list1[x-1], list1[x] = list1[x], list1[x-1]
all_permutations.append(list1)

You are adding multiple references to the same list object to all_permutations, and that list object is modified each time through the loop. Instead, add a copy of the list so that you have a collection of distinct permutations.
all_permutations.append(list1[:])
This is one error; the infinite loop is due to the problem pointed out by IanAuld.

The reason that you new code at http://pastebin.com/bY7ZznR1 gets stuck in an infinite loop is that when len(all_permutations) == 23 becomes True in the inner while loop you then append another list on line 30. And when control gets to the top of the outer loop len(all_permutations) == 24, so the loop continues to execute.
That's easy enough to fix, however your algorithm isn't quite correct. I've modified your code to generate permutations of lists of arbitrary size, and noticed that it gives the right results for lists of length 3 or 4, but not for lists of length 2 or 5; I didn't bother testing other sizes.
FWIW, here's a program that implements the recursive version of the Steinhaus - Johnson - Trotter algorithm. You may find it useful if you want to improve your iterative algorithm.
#!/usr/bin/env python
''' Generate permutations using the Steinhaus - Johnson - Trotter algorithm
This generates permutations in the order known to bell ringers as
"plain changes".
See https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm
From http://stackoverflow.com/q/31209826/4014959
Written by PM 2Ring 2015.07.03
'''
import sys
def sjt_permute(items):
num = len(items)
if num == 1:
yield items[:1]
return
last = items[-1:]
uprange = range(num)
dnrange = uprange[::-1]
descend = True
for perm in sjt_permute(items[:-1]):
rng = dnrange if descend else uprange
for i in rng:
yield perm[:i] + last + perm[i:]
descend = not descend
def main():
num = int(sys.argv[1]) if len(sys.argv) > 1 else 4
items = range(num)
for p in sjt_permute(items):
print(p)
if __name__ == '__main__':
main()
output
[0, 1, 2, 3]
[0, 1, 3, 2]
[0, 3, 1, 2]
[3, 0, 1, 2]
[3, 0, 2, 1]
[0, 3, 2, 1]
[0, 2, 3, 1]
[0, 2, 1, 3]
[2, 0, 1, 3]
[2, 0, 3, 1]
[2, 3, 0, 1]
[3, 2, 0, 1]
[3, 2, 1, 0]
[2, 3, 1, 0]
[2, 1, 3, 0]
[2, 1, 0, 3]
[1, 2, 0, 3]
[1, 2, 3, 0]
[1, 3, 2, 0]
[3, 1, 2, 0]
[3, 1, 0, 2]
[1, 3, 0, 2]
[1, 0, 3, 2]
[1, 0, 2, 3]

Related

Loop while list not empty [duplicate]

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 5 months ago.
I need to loop over a list until list is not empty according to a condition, also
This is the code I wrote but is returning a 'list index out of range' error.
What is the problem? Any suggestions on how to eventually improve this?
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
for i in range(len(l)):
if l[i]>0:
l.remove(l[i])
# store the index processing order
removed.append(i)
else:
continue
l = [x+1 for x in l]
The problem is the list l gets smaller after calling l.remove(value), but subscript 'i' still try to index the original l.
Based on the above analysis, one solution is to keep l unchanged in the inner loop, the other is to keep the unseen i reduced along with l.
# Create new lists to keep `l` unchanged in the inner loop
def method1():
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
next_l = []
[next_l.append(v) if v <= 0 else removed.append(i) for i, v in enumerate(l)]
l = [x+1 for x in next_l]
return removed
def method2():
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
num_del = 0 # record number of deletions in the inner loop
for i in range(len(l)):
if l[i-num_del]>0:
l.remove(l[i-num_del])
num_del += 1
# store the index processing order
removed.append(i)
else:
continue
l = [x+1 for x in l]
return removed
assert method1() == method2()
# output [1, 4, 5, 0, 1, 2]
But I guess you expect the result [1, 4, 5, 0, 2, 3], i.e., record the processing order with subscript in the original list. If so, try this:
l = [0, 1, 0, 0, 1, 1]
el = list(enumerate(l))
removed = []
bound = 0
while len(removed) != len(l):
removed.extend(list(filter(lambda iv: iv[1] > bound, el)))
el = list(filter(lambda iv: iv[1] <= bound, el))
bound -= 1
removed, _ = zip(*removed)
IIUC - it looks like you just want the index of the removed values and keep the values in the original list if they are less than or equal to and then +1 to the value
l = [0, 1, 0, 0, 1, 1]. # your list
keep_idx, lst = zip(*[(idx, i+1) for idx, i in enumerate(l) if i<=0])
print(list(keep_idx)) # -> [0, 2, 3]
print(list(lst)). # -> [1, 1, 1]
Since you're removing items from list while trying to loop through it, your list length keeps going down but the range doesn't account for it. What you could do is looping through the list backwards.
One solution is using
range(len(list_name)-1, -1, -1)
This is to loop through the array backwards and stop the out of range error
EDIT: I was wrong here, but I will keep the answer up so people can see where I went wrong. As Ignatius pointed out, range() is exclusive of the final item.
Original answer:
Lists use Zero-based indexing, so the first item is item 0.
An example list:
l = [1, 2, 3, 4, 5]
l contains 5 items, but indexes start at 0, so '1' is l[0] and '5' is l[4].
This is why you're getting an out of range exception. In your code you are looping up to len(l) which is 6, but l[6] is out of bounds. l[5] is the final item in your list.
Solution:
for i in range(len(l)-1)):

Why is my code not following the command?

def pairs(x):
for num in x:
if num == num :
return x
y = [2, 2, 2, 2, 2, 4, 3, 3]
pairs (y)
print (y)
this is returning [2, 2, 2, 2, 2, 4, 3, 3]
but I want to return [2,2,3]
I've tried 3 codes already other than this one
help me
Your code seems to be intended to find all the numbers that exist in pairs in the list. The best way would be (for a sorted list) to just cycle through the list and check successive elements.
Your code just matches if the current number is the same as the current numbers always returns true and returns all elements. A correct Code might be:
y = [2, 2, 2, 2, 2, 4, 3, 3]
y=sorted(y) # Sorts the given list
def pairs(arr):
i = 0 # Counter variable
res = [] #result list
while i < len(arr)-1:
if arr[i] == arr[i+1]:
res.append(arr[i]) # If the successive elements are the same, add
# it to the result array and since we have already
# checked the next element, increment the counter by
# two
i+=2
else:
i+=1 # If the elements are different we need to check the
# next element as well so increment by 1
return res
print(pairs(y))
You are comparing the element with itself which is always true. Here is the correct logic
y = [2, 2, 2, 2, 2, 4, 3, 3]
filt = []
i=0
while (i< (len(y)-1)):
if y[i] == y[i+1]:
filt.append(y[i])
i+=1
i+=1
print(filt)

Move zeroes to end of list

I am working on moving all zeroes to end of list. .. is this approach bad and computationally expensive?
a = [1, 2, 0, 0, 0, 3, 6]
temp = []
zeros = []
for i in range(len(a)):
if a[i] !=0:
temp.append(a[i])
else:
zeros.append(a[i])
print(temp+zeros)
My Program works but not sure if this is a good approach?
A sorted solution that avoids changing the order of the other elements is:
from operator import not_
sorted(a, key=not_)
or without an import:
sorted(a, key=lambda x: not x) # Or x == 0 for specific numeric test
By making the key a simple boolean, sorted splits it into things that are truthy followed by things that are falsy, and since it's a stable sort, the order of things within each category is the same as the original input.
This looks like a list. Could you just use sort?
a = [1, 2, 0, 0, 0, 3, 6]
a.sort(reverse=True)
a
[6, 3, 2, 1, 0, 0, 0]
To move all the zeroes to the end of the list while preserving the order of all the elements in one traversal, we can keep the count of all the non-zero elements from the beginning and swap it with the next element when a non-zero element is encountered after zeroes.
This can be explained as:
arr = [18, 0, 4, 0, 0, 6]
count = 0
for i in range(len(arr):
if arr[i] != 0:
arr[i], arr[count] = arr[count], arr[i]
count += 1
How the loop works:
when i = 0, arr[i] will be 18, so according to the code it will swap with itself, which doesn't make a difference, and count will be incremented by one. When i=1, it will have no affect as till now the list we have traversed is what we want(zero in the end). When i=4, arr[i]= 4 and arr[count(1)]= 0, so we swap them leaving the list as[18, 4, 0, 0, 0, 6] and count becomes 2 signifying two non-zero elements in the beginning. And then the loop continues.
You can try my solution if you like
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
for num in nums:
if num == 0:
nums.remove(num)
nums.append(num)
I have tried this code in leetcode & my submission got accepted using above code.
Nothing wrong with your approach, really depends on how you want to store the resulting values. Here is a way to do it using list.extend() and list.count() that preserves order of the non-zero elements and results in a single list.
a = [1, 2, 0, 0, 0, 3, 6]
result = [n for n in a if n != 0]
result.extend([0] * a.count(0))
print(result)
# [1, 2, 3, 6, 0, 0, 0]
You can try this
a = [1, 2, 0, 0, 0, 3, 6]
x=[i for i in a if i!=0]
y=[i for i in a if i==0]
x.extend(y)
print(x)
There's nothing wrong with your solution, and you should always pick a solution you understand over a 'clever' one you don't if you have to look after it.
Here's an alternative which never makes a new list and only passes through the list once. It will also preserve the order of the items. If that's not necessary the reverse sort solution is miles better.
def zeros_to_the_back(values):
zeros = 0
for value in values:
if value == 0:
zeros += 1
else:
yield value
yield from (0 for _ in range(zeros))
print(list(
zeros_to_the_back([1, 2, 0, 0, 0, 3, 6])
))
# [1, 2, 3, 6, 0, 0, 0]
This works using a generator which spits out answers one at a time. If we spot a good value we return it immediately, otherwise we just count the zeros and then return a bunch of them at the end.
yield from is Python 3 specific, so if you are using 2, just can replace this with a loop yielding zero over and over.
Numpy solution that preserves the order
import numpy as np
a = np.asarray([1, 2, 0, 0, 0, 3, 6])
# mask is a boolean array that is True where a is equal to 0
mask = (a == 0)
# Take the subset of the array that are zeros
zeros = a[mask]
# Take the subset of the array that are NOT zeros
temp = a[~mask]
# Join the arrays
joint_array = np.concatenate([temp, zeros])
I tried using sorted, which is similar to sort().
a = [1, 2, 0, 0, 0, 3, 6]
sorted(a,reverse=True)
ans:
[6, 3, 2, 1, 0, 0, 0]
from typing import List
def move(A:List[int]):
j=0 # track of nonzero elements
k=-1 # track of zeroes
size=len(A)
for i in range(size):
if A[i]!=0:
A[j]=A[i]
j+=1
elif A[i]==0:
A[k]=0
k-=1
since we have to keep the relative order. when you see nonzero element, place that nonzero into the index of jth.
first_nonzero=A[0] # j=0
second_nonzero=A[1] # j=1
third_nonzero=A[2] # j=2
With k we keep track of 0 elements. In python A[-1] refers to the last element of the array.
first_zero=A[-1] # k=-1
second_zero=A[-2] # k=-2
third_zero= A[-3] # k=-3
a = [4,6,0,6,0,7,0]
a = filter (lambda x : x!= 0, a) + [0]*a.count(0)
[4, 6, 6, 7, 0, 0, 0]

Return lists that do not have 1s

I want to create what I thought was a fairly straightforward function. The function just runs through a list of lists and returns any list that does not have a 1 in all of the list elements following the second element ([2: ]). So given the list of lists [[1, 2, 1, 1, 1, 1], [4, 5, 1, 2, 0.3, 1, 1, 1]] the function would return [4, 5, 1, 2, 0.3, 1, 1, 1]. What I have so far is:
def discover(A):
"""Looks for list that has an element not equal to one.
"""
for i in range(len(A)):
for j in range(len(A[i])):
if A[i][j+2] != 1:
print A[i]
But when I run the function it finds one list but then prints that list over and over again before giving me an IndexError saying the list index is out of range. This seems to be a fairly easy problem but for some reason I'm not getting it. Any help would be really appreciated.
The problem is these two lines:
for j in range(len(A[i])):
if A[i][j+2] != 1:
What'll happen is that you'll eventually get to a point where j is the length of your list, minus 1. But then you're calling j+2 in the below code, and that's guaranteed to create a number longer than your list, giving you the IndexError. You can fix that with:
for j in range(2,len(A[i])):
if A[i][j] != 1:
As for the endless printing, you're nearly there, but you'll want to stop the loop if you find the non-1 element.
if A[i][j] != 1:
print A[i]
break
Alternately, the other answers will give you the same result more easily. But that's where your current errors are coming from.
for list in A:
if 1 not in list[3:]:
print list
even another solution:
lst = [
[1,2,3],
[1,1,1],
[3,4,5],
[3,5,6],
] # +++
def has1(subLst):
return subLst.count(1) == 0
print filter(has1, lst)
This avoids out of range issues.
def discover(A):
results = []
for lst in A:
for i in lst[3:]:
if i != 1:
results.append(lst)
break
return results
In addition to the other answers here, one could also make use of a generator. The yield statement will allow you to skirt establishing a default list to place your results into; you can just specify the condition you're looking for and yield the result.
>>> def discover(lists):
... for l in lists:
... if not [x for x in l[2:] if x != 1]:
... yield l
>>> stuff = [[2, 3, 4, 5, 1, 2], [2, 5, 1, 1, 1, 1, 1]]
>>> results = discover(stuff) #returns <generator object discover at 0x105e3eb90>
>>> results.next()
[2, 5, 1, 1, 1, 1, 1]
>>>
The magic line here being, if not [x for x in l[2:] if x !=1]. It builds a list from l[2:] and checks that any variable in there does not equal 1; if the list has no length, it means there are no non-1 entries in l[2:] and so it yields l.
A query to check if any element (after the second) != 1 would be:
any(x != 1 for x in mylist[3:])
so
def discover(A):
for mylist in A:
if any(x != 1 for x in mylist[3:]):
print mylist

Transform a flat list to nested List

Using Python
I want the following:
[1, 2, 2, 1, 2, 3, 2, 3]
To be transformed into:
[1, 2, 2, [1, 2, 3], 2, 3]
Rules: Go through each item in the list. If we hit a 2 followed by a 1 created a list and include that 1 in that list until we hit a 3, include that 3, then close the list and continue. It's like if 1 was 3 were parenthesis.
I'm not very good with recursive algorithms which I think might be needed in this case.
Thanks as always.
Still keeping in mind that #Walter is correct in his comments to your question, this is a silly implementation of what you asked for, inspired by the final bit of your question, in which you suggest that 1 and 3 could just be replaced with [1 and 3].
>>> import re
>>> s = repr([1, 2, 2, 1, 2, 3, 2, 3])
>>> s = re.sub('1', '[1', s)
>>> s = re.sub('3', '3]', s)
>>> l = eval(s)
>>> l
[[1, 2, 2, [1, 2, 3], 2, 3]]
What it does is working on the representation of the list (a string) and substituting the way you suggested using regular expressions. Finally, it evaluate the string (getting back a list).
I call this implementation "silly" because it does the trick, but it's ugly and truly unpythonic. That said, it does the trick, so if you are simply using it for a one-off conversion of some data you need to use...
HTH!
def whatever(a):
b = []
tmp = []
last = None
for elem in a:
if tmp:
tmp.append(elem)
if elem == 3:
b.append(tmp)
tmp = []
elif last == 2 and elem == 1:
tmp.append(1)
else:
b.append(elem)
last = elem
return b
print whatever([1, 2, 2, 1, 2, 3, 2, 3])
That is an entertaining problem! Here is my solution:
def treeize(treeizable, tree=None, stopper=object()):
if tree is None:
tree = []
if treeizable[:1] == [stopper]:
tree.append(treeizable.pop(0))
return tree
elif treeizable[0:2] == [2, 1]:
tree.append(treeizable.pop(0))
subtree = []
treeize(treeizable, subtree, stopper=3)
tree.append(subtree)
return treeize(treeizable, tree, stopper)
elif treeizable:
tree.append(treeizable.pop(0))
return treeize(treeizable, tree, stopper)
else:
return tree
This function receives a flat list treeizable that should be converted to a nested list tree. The stopper parameter marks when the current list is done - being this a nested or the toplevel list. (Since the default value of stopper is an instance of object, it is impossible that there will be a stopper on a list called with the default value, because instances of object are different between themselves).
def treeize(treeizable, tree=None, stopper=object()):
For easing our work, the default value of tree is None and, if it has the default value, then it is set to a list. It is made because it is problematic to have mutable values as default parameter objects. Also, it would be annoying to have to type the function with an empty list everytime.
if tree is None:
tree = []
If the first value of the flat list is the "stopper", then it is added to the tree and the tree is returned. Note that, by using treeizable.pop(0) I am actually removing the value from the flat list. Since the stopper is only set when defining a nested list so, when we found it, no more need to be done: the "subtree" (that is, the nested list) is complete. Also, note that I get the first element of the list with a slice of the list. I made it because it is boring to have to type if treeizable and treeizable[0] == stopper. Since slicing does not have problems with inexistent indexes, I got the slice and compared it to another list made in place with only the stopper:
if treeizable[:1] == [stopper]:
tree.append(treeizable.pop(0))
return tree
If the beginning of the list is the "beginner", then I pop the first element from the list, append it to the tree and create a new tree - that is, a empty list. Now I call treeize() with the remaining list and the empty subtree, also passing 3 as the stopper value. treeize() will recursively generate a new tree that I append to my initial tree. After that, just call treeize() with the remaining of the list (which does not contain the elements of the subtree anymore) and the original list. Note that the stopper should be the same stopper received by the original call.
elif treeizable[0:2] == [2, 1]:
tree.append(treeizable.pop(0))
subtree = []
treeize(treeizable, subtree, stopper=3)
tree.append(subtree)
return treeize(treeizable, tree, stopper)
If none of the previous conditions (the first element is a stopper, the beginning of the list is [2, 1]) is true, then I verify if there is something in the list. In this case, I pop the first element, add to the tree and call treeize() with the rest of the list.
elif treeizable:
tree.append(treeizable.pop(0))
return treeize(treeizable, tree, stopper)
In the case that not even the previous condition is true... then we have an empty list. This means that all elements were put in the tree. Just return the tree to the user:
else:
return tree
This seems to have worked:
>>> treeize.treeize([1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 3, 2, 4, 5, 3, 3, 2, 3, 4])
[1, 2, 2, [1, 2, 2, [1, 2, 2, [1, 2, 3], 2, 4, 5, 3], 3], 2, 3, 4]
Your question has a taste of homework. In principle, we should not answer it but it is so interesting that I couldn't help myself :) If it is a homework, however, do not try to use this solution as your because it would be wrong and your teacher would surely find it on Google :P
I like state machines:
from itertools import izip, tee
def pairwise(iterable):
a, b = tee(iterable)
next(b)
return izip(a, b)
class Flat(object):
def append_next(self, alist, e0, e1):
alist.append(e0)
if e0 == 2 and e1 == 1:
alist.append([])
self.__class__ = Nested
def append_last(self, alist, e):
alist.append(e)
class Nested(object):
def append_next(self, alist, e0, e1):
alist[-1].append(e0)
if e0 == 3:
self.__class__ = Flat
def append_last(self, alist, e):
alist[-1].append(e)
def nested(flat_list):
if len(flat_list) <= 1:
return list(flat_list)
state = Flat()
nested_list = []
for x, y in pairwise(flat_list):
state.append_next(nested_list, x, y)
state.append_last(nested_list, y)
return nested_list
s = [1, 2, 2, 1, 2, 3, 2, 3]
print nested(s)
gives:
[1, 2, 2, [1, 2, 3], 2, 3]
But this might be more pythonic:
def nested(flat_list):
if len(flat_list) <= 1:
return list(flat_list)
pairs = pairwise(flat_list)
nested_list = []
while True:
for x, y in pairs:
nested_list.append(x)
if x == 2 and y == 1:
nested_list.append([])
break
else:
nested_list.append(y)
break
for x, y in pairs:
nested_list[-1].append(x)
if x == 3:
break
else:
nested_list[-1].append(y)
break
return nested_list
Pease bear with me - it is 2:50(night) - here is my version - not very beatiful but it works pretty well for me:
def buildNewList(inputList):
last = 0
res = []
for i,c in enumerate(inputList):
if i == 0:
prev = c
if i < last:
continue
if c == 1 and prev == 2:
if 3 in inputList[i:]:
last = i + 1 + inputList[i:].index(3)
res.append(buildNewList(inputList[i: last]))
else:
last = len(inputList)
res.append(buildNewList(inputList[i:len(inputList)]))
else:
res.append(c)
prev = c
return res
l1 = buildNewList([1, 2, 2, 1, 2, 3, 2, 3])
>>> [1, 2, 2, [1, 2, 3], 2, 3]
l2 = buildNewList([1, 2, 2, 1, 2, 3, 2, 1, 2, 3])
>>> [1, 2, 2, [1, 2, 3], 2, [1, 2, 3]]
l3 = buildNewList([1,2,3,1,2,3])
>>> [1, 2, 3, 1, 2, 3]
l4 = buildNewList([1,2,1,1,2,1])
>>> [1, 2, [1, 1, 2, [1]]]

Categories

Resources