Alternate Python List Reverse Solution Needed - python

I had a job interview today. During it I was asked to write down an algorithm that will reverse a list. First I offered the answer using the reversed() method:
x = [1,2,3,4,5]
y = reversed(x)
for i in y:
print i
The senior developer conducting the interview asked me if I know another way, upon which I wrote down the other known method with slicing:
x = [1,2,3,4,5]
y = x[::-1]
Unfortunately he was unhappy with this solution as well and asked me to think of another one. After few minutes I said I could not come up with a better one. He said that this was not good enough for their standards.
I am perfectly fine with his opinion and have no problem practicing more on my code. My question is, what is a better solution that I am not aware of, if there is one. Could there be some other more 'programmer' way...Only other thing that comes to mind is recursion, however I thought of it only after the interview was already done.
Thanks.

Both your answers are good in terms of python so the interviewer must have been asking you to implement your own method:
Using recursion:
def recur_rev(l):
return recur_rev(l[1:]) + l[:1] if l else l
Or a list comp and range starting at the length of l -1 and going in reverse:
l = list(range(100))
print([l[ind] for ind in range(len(l)-1,-1,-1)])
Using itertools.count:
from itertools import count
cn = count(len(l) -1, -1)
print([l[next(cn)] for ele in l])
For efficiency use a generator expression:
rev = (l[next(cn)] for ele in l)
for ele in rev:
print(ele)
Or using map:
print(list(map(l.__getitem__,range(len(l)-1,-1,-1)))) # list needed for python3
[99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
without the list call on map we will get a map object we can iterate over in python3, you can use itertools.imap in python2 to achieve a similar result

I guess your interviewer wanted to hear something like this:
A straightforward way to revert a list is to find out its length, then iterate from len-1 downto 0 and append the i-th element to the result. This approach works fine with real lists, but how can we revert a generator, i.e. something whose length is not known and cannot be found? (Good pythonistas use generators and yields instead of lists and returns).
Here's where we need a data structure known as "stack". A stack is like, well, a stack of things, like plates, put on the top of each other.
Whatever you put last comes out first, whatever was put first comes out last. So, our strategy will be like this:
while there are items, put each item onto the stack
while the stack is not empty, pop items off the stack and return them
Stacks can be programmed in python using lists, where .append puts an item on the top of the stack, and .pop removes the last thing off the stack:
def reverse(it):
stack = []
for item in it:
stack.append(item)
while stack:
yield stack.pop()
Of course, Python already has a built-in stack, and it's the stack of call frames. We can use that instead of the simulated one above, which leads us to the following recursive solution:
def reverse(it):
head = next(it)
for item in reverse(it):
yield item
yield head
in python3, this is even more elegant:
def reverse(it):
head = next(it)
yield from reverse(it)
yield head
Again, this works with arbitrary iterables, whose length is unknown at the call time.

One simple algorithm that could be easily ported to other languages would be:
x = [1,2,3,4,5]
y = []
for i in len(x):
y.append(x[len(x) - 1 - i])
Or using an iterator. This could be easily ported to be used with a chained list where you don't know the length:
x = [1,2,3,4,5]
y = []
x_iterator = iter(x)
try:
while (1):
y.insert(0, x_iterator.next())
except:
print y
Or a more pythonic version with insert:
x = [1,2,3,4,5]
y = []
for elem in x:
y.insert(0, elem)

I'm assuming your interviewer didn't want you to use built-in Python methods, but an algorithm that's more language-agnostic. Something like:
lst = [1,2,3,4,5]
lst2 = []
while len(lst) > 0:
lst2.append(lst.pop())
or
lst2 = [lst.pop() for _ in lst[:]]

How about something like that:
x = [1, 2, 3, 4, 5]
for i in xrange(len(x) / 2):
x[i], x[-i - 1] = x[-i - 1], x[i]
print(x)
The idea is to swap array elements from opposite directions

I like this way:
def reverse(arr):
for i in range(len(arr) / 2):
arr[-i-1], arr[i] = arr[i], arr[-i-1]
Basically iterating through the first half of the array and swapping i and len(i)-i-1.

def reverse(text):
rev = []
for let in range(len(text),0,-1):
rev.append(text[let-1])
return ''.join(rev)

Related

Selecting leftmost unique elements from multiple lists until given size is reached

I have the following lists:
a = [ 1, 6, 76, 15, 46, 55, 47, 15, 72, 58, ..] # there could be more than 10 elements in each
b = [17, 48, 22, 7, 35, 19, 91, 85, 49, 35, ..]
c = [46, 8, 53, 49, 28, 82, 30, 86, 57, 9, ..]
d = [82, 12, 24, 60, 66, 17, 13, 69, 28, 99, ..]
e = [ 1, 53, 17, 82, 21, 20, 88, 10, 82, 41, ..]
I want to write a function which takes any number of those list (could be all, could be only a and c for example) as its argument and selects the leftmost unique 10 elements equally from every list. For example, I will show in pictures with.
The initial data we have (length of 10 assumption).
We look at the first elements of every row and see a and e have same values. We randomly select let's say e, remove that element and shift it to the left and get this
Here we see that there is again overlap, 17 is appearing already and we shift e one more time
Again similar problem and we shift it one last time
Finally, we can select the first two elements of each list and there will be no duplicates
[1, 6, 17, 48, 46, 8, 82, 12, 21, 53]
It could be that more than one list could have identical values, same rules should apply.
I came with this which and for solving randomness I decided to shuffle the list before using it:
def prepare_unique_array(
arrays: list = [],
max_length: int = 10,
slice_number: int = 2
):
unique_array = []
for array in arrays:
for i in range(slice_number):
while not len(unique_array) == max_length:
if array[i] not in unique_array:
unique_array.append(array[i])
else:
while array[i+1] in unique_array:
i += 1
unique_array.append(array[i+1])
return unique_array
Which gives the desired result given those initial values, but anything changes and it does not work.
maybe there is a numpy approach which does it faster and easier as well.
I will appreciate any guide/help
Using cycle and iter to pick one element from each iterable, alternately:
from itertools import cycle
def uniques_evenly(n, *iterables):
its = cycle(iter(seq) for seq in iterables)
seen = set()
it = next(its)
for _ in range(n):
x = next(it)
while x in seen:
x = next(it) # pick next unique number
seen.add(x)
yield x
it = next(its) # switch to next iterator
Note that this will crash if one of the iterators is too short.
Testing:
a = [ 1, 6, 76, 15, 46, 55, 47, 15, 72, 58, 37756, 712, 666]
b = [17, 48, 22, 7, 35, 19, 91, 85, 49, 35, 42]
c = [46, 8, 53, 49, 28, 82, 30, 86, 57, 9]
d = [82, 12, 24, 60, 66, 17, 13, 69, 28, 99]
e = [ 1, 53, 17, 82, 21, 20, 88, 10, 82, 41, 216]
print( list(uniques_evenly(10, a,b,c,d,e)) )
# [1, 17, 46, 82, 53, 6, 48, 8, 12, 21]
Explanations
We use iter() to transform a list into an iterator. An iterator is something that "consumes" values and returns them, at every call of next(). For instance:
l = [3, 4, 7] # l is a list
i = iter(l) # i is an iterator
print( next(i) )
# 3
print( next(i) )
# 4
print( next(i) )
# 7
print( next(i) )
# raises exception StopIteration
Then, we use itertools.cycle to alternate between the five iterators. cycle returns an infinite iterator that cycles between the items in the list we gave it. We gave it a list of five iterators:
its = cycle(iter(seq) for seq in iterables)
This is the same thing as if we had written:
its = cycle([iter(a), iter(b), iter(c), iter(d), iter(e)]
Here is a demonstration with only two iterators instead of 5:
a = ['hello', 'world']
b = [5, 12]
its = cycle([iter(a), iter(b)])
it = next(its) # it is now the iterator on a
print( next(it) ) # 'hello'
it = next(its) # it is now the iterator on b
print( next(it) ) # 5
it = next(its) # it cycles back to a
print( next(it) ) # 'world'
it = next(its) # it is now b
print( next(it) ) # 12
it = next(its) # it is a
print( next(it) ) # raises exception StopIteration
So this is essentially what happens in uniques_evenly.
In addition, we use a set seen to remember the elements we have already seen. Sets are cool because testing for membership is a fast operation: if x in seen: is a constant-time operation, it doesn't matter how big seen is.
Now it is one of the five iterators, say the iterator on list d. We pick the next element in d with x = next(it); then we run the loop:
while x in seen:
x = next(it)
Which means: if x is an element already seen previously, then pick the next element in d. Keep picking the next element until you find an element that wasn't seen previously. Once we've finally picked an element x that hasn't been seen previously, we add it to set seen so it won't be picked again in the future, and then:
yield x
This is a bit special. It means uniques_evenly is not a function; if it was a function, we would have used the keyword return and not yield. Instead, uniques_evenly is a generator. Instead of using this syntax with yield, the other alternative would have been to declare a list result, initially empty, then instead of yield x, I would have written result.append(x); then at the very end of the function, return result. Then uniques_evenly would have been a function that returns a list.
The difference between a function that returns a list, and a generator, is a bit subtle. Basically a generator behaves like a lazy list, whose elements are computed and produced only when needed.
When testing my code, I immediately converted the generator to a list anyway:
print( list(uniques_evenly(10, a,b,c,d,e)) )
So the difference doesn't matter. If you're more comfortable with having a variable result and using result.append(x) instead of yield x, then returning result at the end of the function, you can do it this way instead.

Python: Is there a way to save random numbers in the list as sorted?

I have a code that stores 9 random numbers in the list.
now, I want to sort the values in the list object.
I found two method. but these methods have some problems.
This is my code 1:
import random
array = []
for i in range(1, 10):
array.append(random.randrange(i, 100))
array.sort()
print(array)
output:
[14, 23, 31, 33, 50, 65, 86, 96, 99]
This code works well, but must be executed after the 'for loop'. I want to execute sort in the 'for loop'.
This is my code 2:
import random
array = []
for i in range(1, 10):
array.append(random.randrange(i, 100))
array.sort()
print(array)
output:
[8, 22, 23, 26, 39, 48, 51, 53, 71]
This code works well, but you will run the sort nine times.
Is there any good way?
Why would it matter to execute the sort after the for-loop?
The number of comparisons is basically the same.
It is actually better to sort it afterward to avoid unnecessary comparisons.
In case you want a two-liner on how to do this with list comprehension:
array = [random.randrange(i, 100) for i in range(10)]
array.sort()
If I really understand your meaning, you want to get an sorted array during the loop for each iterative time, that is to say, you want to do something insert into sorted array. bisect can help here.
Following code will ensure array are always sorted in the loop, but not sort each time, it binary search and insert the new element into the array:
import random
import bisect
array = []
for i in range(1, 10):
bisect.insort(array, random.randrange(i, 100))
print (sorted([random.randrange(i, 100) for i in range(10)]))
Output:
[8, 26, 48, 62, 68, 77, 78, 83, 94, 96]

python lambda for list_of_list collapse

OK, newbie getting in knots with a list_of_list collapse.
I already figured out that I can reduce the for loops below to the comprehension.
lol = [[0, 1, 2, 3, 4, 50, 51, 52, 53, 54], \
[50, 51, 52, 53, 54], \
[150, 151, 152, 153, 154]]
collapsed_lol = list()
for innerlist in lol:
for item in innerlist:
collapsed_lol.append(item)
collapsed_lol = [item for innerlist in lol for item in innerlist]
But then I tried a reduce(lambda) approach and got unstuck. Why does this not work?
collapsed_lol = reduce(lambda a,x: a.extend(x), lol)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 1, in <lambda>
# AttributeError: 'NoneType' object has no attribute 'extend'
# lol
# [[0, 1, 2, 3, 4, 50, 51, 52, 53, 54, 50, 51, 52, 53, 54], [50, 51, 52, 53, 54], [150, 151, 152, 153, 154]]
# so lol[0].append[lol[1]] did occur, but then stopped.
If I assign the lambda to a variable instead of inside reduce, it does the same, successfully doing lol[0].extend[lol[1]]; the return is None.
f = lambda a,b: a.extend(b)
lst_1 = [0, 1, 2, 3, 4, 5]
lst_2 = [50, 51, 52, 53, 54]
ret=f(lst_1,lst_2)
ret # is nothing
lst_1
# [0, 1, 2, 3, 4, 5, 50, 51, 52, 53, 54]
lst_2
# [50, 51, 52, 53, 54]
print f(lst_1,lst_2)
# None
This is the same behavior as in the nested reduce contruct.
Clearly I'm trying something that doesn't work, not the way lambda's are supposed to be used - but can someone explain what the None represents, what the process involved here is? thanks
The function that you pass to reduce has to return the value of fn(a,b). Since extend() returns None, then you can't use the lambda that you have.
You can force the lambda to return the value by changing it to:
fn = lambda a,b: a.extend(b) or a
and calling reduce(fn, lol) will return the value of all the lists in 'lol' appended together. Note though what happens. You will also have modified the first element of lol to be this summed value. Why? Because the first call to reduce() uses the first 2 elements of the given sequence for a and b, calls fn(a,b), and uses that returned value as the a for the next call, and the next element in the list for b, and so on until the list is exhausted. If it were written in Python it would look something like:
def reduce(fn, seq):
seq_iter = iter(seq)
a = next(seq_iter)
for b in seq_iter:
a = fn(a,b)
return a
Since extend() updates the list in place, you will repeatedly extend that lol[0] element.
You may be better off just using sum(lol, []) to flatten your list-of-lists. It has some memory warts relative to making many copies of intermediate lists, but for short lists like this it is very simple and gets the job done.
If you are absolutely set on using reduce and don't want to modify the contents of lol, just use reduce(lambda a,b: a+b, lol). This will also generate the many intermediate lists, but it won't modify the contents of lol (because when a and b are lists, a+b returns a new list, it doesn't modify in place like extend()).
that's just a function that's return None.
f = lambda a,b: a.extend(b)
This function only modify a.
And it's the same than:
def fct(a, b):
return a.extend(b)

Trying to write an Eratosthenes Sieve in Python. Is this correct and how can I make it faster? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Fastest way to list all primes below N in python
I have not been doing programming for very long, and I'm just doing this for fun, and I don't know much advanced Python, but...
I wrote this, and I wanted to know whether it is actually an Eratosthenes Sieve program, and if it is, how could I make it faster. I don't really want someone to post a program that is a solution, but more tell me how I could adapt mine.
def eratSieve(n):
all = []
for a in range(2, n+1):
all.append(a)
for b in all:
for i in range(2,int(round(len(all)/b))):
while i*b in all:
all.remove(i*b)
i+=1
return all
Thanks for your help.
BTW - It's in Python 2.7
It does not work right.
The main problem is that you loop on all the value in all and in the while you remove some element from all.
This way some value in all are not considered, so the function does not remove all the non-prime numbers
Try to execute it for n=100 and the result you get is
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99
while it should be
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
(from http://en.wikipedia.org/wiki/Prime_number)
Also, the range of the second for is wrong, since you consider the lenght of the list, not the current value of b and so you check for multiple of 2 only in the first 50 values, the multiple of 3 in the first 17, 5 in the first 9 and so on. From b = 13 you never enter in the inner for, since int(round(len(all)/b)) = 1 and so you have something like for i in range(2,1)
I agree with Gianluca, and I have a possible solution: keep your main array (all) as bools and mark non-primes, but don't remove them. It might also be faster, because you don't change list size.
Minor thing: you can just write all = range(2, n+1) in the beginning if you want to keep it as ints.
Your method produces incorrect results. The error lies in the for i loop. Here it is, adjusted, and with a test:
known_primes = [
2,3,5,7,11,13,17,19,23,29,
31,37,41,43,47,53,59,61,67,71,
73,79,83,89,97,101,103,107,109,113,
127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211,223,227,229,
233,239,241,251,257,263,269,271,277,281,
283,293,307,311,313,317,331,337,347,349,
353,359,367,373,379,383,389,397,401,409,
419,421,431,433,439,443,449,457,461,463,
467,479,487,491,499,503,509,521,523,541,
547,557,563,569,571,577,587,593,599,601,
607,613,617,619,631,641,643,647,653,659,
661,673,677,683,691,701,709,719,727,733,
739,743,751,757,761,769,773,787,797,809,
811,821,823,827,829,839,853,857,859,863,
877,881,883,887,907,911,919,929,937,941,
947,953,967,971,977,983,991,997,1009,1013,
1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,
1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,
1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,
1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,
1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,
1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,
1453,1459,1471,1481,1483,1487,1489,1493,1499]
def eratSieve(n):
all = []
for a in range(2, n+1):
all.append(a)
for b in all:
for i in all[all.index(b):]:
while i*b in all:
all.remove(i*b)
i+=1
return all
for N in range(1500):
for n in eratSieve(N):
if n not in known_primes:
print N,n
def primes(N):
primes = [x for x in (2, 3, 5, 7, 11, 13) if x < N]
if N < 17: return primes
candidators = [x for x in xrange((N - 2) | 1, 15, -2)
if x % 3 and x % 5 and x % 7 and x % 11 and x % 13]
top = int(N ** 0.5)
while (top + 1) * (top + 1) <= N: top += 1
while True:
p = candidators.pop()
primes.append(p)
if p > top: break
candidators = filter(p.__rmod__, candidators)
candidators.reverse()
primes.extend(candidators)
return primes
I think this code would work faster...

What's the best way of skip N values of the iteration variable in Python?

In many languages we can do something like:
for (int i = 0; i < value; i++)
{
if (condition)
{
i += 10;
}
}
How can I do the same in Python? The following (of course) does not work:
for i in xrange(value):
if condition:
i += 10
I could do something like this:
i = 0
while i < value:
if condition:
i += 10
i += 1
but I'm wondering if there is a more elegant (pythonic?) way of doing this in Python.
Use continue.
for i in xrange(value):
if condition:
continue
If you want to force your iterable to skip forwards, you must call .next().
>>> iterable = iter(xrange(100))
>>> for i in iterable:
... if i % 10 == 0:
... [iterable.next() for x in range(10)]
...
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
As you can see, this is disgusting.
Create the iterable before the loop.
Skip one by using next on the iterator
it = iter(xrange(value))
for i in it:
if condition:
i = next(it)
Skip many by using itertools or recipes based on ideas from itertools.
itertools.dropwhile()
it = iter(xrange(value))
for i in it:
if x<5:
i = dropwhile(lambda x: x<5, it)
Take a read through the itertools page, it shows some very common uses of working with iterators.
itertools islice
it = islice(xrange(value), 10)
for i in it:
...do stuff with i...
It's a very old question, but I find the accepted answer is not totally stisfactory:
first, after the if ... / [next()...] sequence, the value of i hasn't changed. In your first example, it has.
second, the list comprehension is used to produce a side-effect. This should be avoided.
third, there might be a faster way to achieve this.
Using a modified version of consume in itertools recipes, you can write:
import itertools
def consume(it, n):
return next(itertools.islice(it, n-1, n), None)
it = iter(range(20))
for i in it:
print(i, end='->')
if i%4 == 0:
i = consume(it, 5)
print(i)
As written in the doctstring of consume, the iterator is consumed at C speed (didn't benchmark though). Output:
0->5
6->6
7->7
8->13
14->14
15->15
16->None
With a minor modification, one can get 21 instead of None, but I think this isnot a good idea because this code does work with any iterable (otherwise one would prefer the while version):
import string
it = iter(string.ascii_lowercase) # a-z
for x in it:
print(x, end="->")
if x in set('aeiouy'):
x = consume(it, 2) # skip the two letters after the vowel
print(x)
Output:
a->c
d->d
e->g
h->h
i->k
l->l
m->m
n->n
o->q
r->r
s->s
t->t
u->w
x->x
y->None
Itertools has a recommended way to do this: https://docs.python.org/3.7/library/itertools.html#itertools-recipes
import collections
def tail(n, iterable):
"Return an iterator over the last n items"
# tail(3, 'ABCDEFG') --> E F G
return iter(collections.deque(iterable, maxlen=n))
Now you can do:
for i in tail(5, range(10)):
print(i)
to get
5
6
7
8
9
I am hoping I am not answering this wrong... but this is the simplest way I have come across:
for x in range(0,10,2):
print x
output should be something like this:
0
2
4
6
8
The 2 in the range parameter's is the jump value
Does a generator function here is rebundant?
Like this:
def filterRange(range, condition):
x = 0
while x < range:
x = (x+10) if condition(x) else (x + 1)
yield x
if __name__ == "__main__":
for i in filterRange(100, lambda x: x > 2):
print i
There are a few ways to create iterators, but the custom iterator class is the most extensible:
class skip_if: # skip_if(object) for python2
"""
iterates through iterable, calling skipper with each value
if skipper returns a positive integer, that many values are
skipped
"""
def __init__(self, iterable, skipper):
self.it = iter(iterable)
self.skip = skipper
def __iter__(self):
return self
def __next__(self): # def next(self): for python2
value = next(self.it)
for _ in range(self.skip(value)):
next(self.it, None)
return value
and in use:
>>> for i in skip_if(range(1,100), lambda n: 10 if not n%10 else 0):
... print(i, end=', ')
...
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
I think you have to use a while loop for this...for loop loops over an iterable..and you cannot skip next item like how you want to do it here

Categories

Resources