in Python, range(1, 100, *10) doesn't work? - python

I have a problem with range() or the for loop.
in C++, the for loop can be used like this:
for(int i=1;i<=100;i*=10){
//lala
}
My Python script that causes the error looks like this:
li = [x for x in range(100, 0, /10)]
print(li)
I hope to get li = [100, 10, 1]
How can I achieve this without the use of li.append ( 100, 10, 1 )?

You can't use range for this. The third argument for range is a fixed step used to increment the value: it can't be used to multiply or divide the previous value.
Instead, use a while loop:
i = 1
while i < 100:
...
i *= 10 # or i /= 10 depending on what you want.
If you really want to use a list comprehension, you can do as suggested by Albin Paul:
li = [10**x for x in range(3) ] # [1, 10, 100]
li = [10**x for x in range(2, -1, -1) # [100, 10, 1]

You can simply use a generator expression, like
for i in (10 ** x for x in range(2, -1, -1)):
print(i)
# 100
# 10
# 1
Or, if you want your "power range" to be a reusable function, you can create your own, i.e.
def powrange(*args, base=10):
for i in range(*args):
yield base ** i
for i in powrange(2, -1, -1, base=10):
print(i)
# 100
# 10
# 1

The built-in range is not meant to be used this way. It takes three arguments: range(start, stop, step) and goes from start to stop with a constant stepsize.
If you want to use a single function, you could use numpy's logspace
It looks like this:
import numpy
li = numpy.logspace(0, 2, base=10, dtype = int)
# li is [1, 10, 100]
# goes from base^0 to base^2, so from 1 to 100
If you don't want to rely on numpy, the solution with a while loop seems very pythonic to me.
Benchmark time :D
A comment pointed out that this solution might be:
non obvious
slow
The first point is a matter of taste, but I disagree on performance:
reps = 100000
list_elems = 100
start = time.perf_counter()
for i in range(reps):
np.logspace(0, list_elems, base=2, dtype=int)
end = time.perf_counter()
numpy_time = end - start
start = time.perf_counter()
for i in range(reps):
[2**x for x in range(list_elems)]
end = time.perf_counter()
python_time = end - start
# reps = 100000
# list_elems = 100
# numpy took 1.70, python took 2.86
# reps = 100000
# list_elems = 10
# numpy took 1.65, python took 0.242
Of course, this depends a lot on the number of list elements that you want to produce. Numpy introduces overhead but scales better. So for shorter lists, python wins.

To change make a log(n) time complexity loop you need the log function or use while loops to do the job
In [10]: import math
In [9]: [10**x for x in range( 0,int(math.log10(1000)))]
Out[9]: [1, 10, 100]
I still think while loops are the way to go

The arguments for range are: start, stop and step, where the step is int.
Maybe you want to see the result as this:
li = [10**x for x in range(0, 3, 1)]
print(li)
[1, 10, 100]
Overall take a look at a nice learning course, for example, "Automate the Boring Stuff with Python":
Here is the video right about loops
And here is the whole
course (available online for free)

When you use the for loop as
with x in range(start,stop,step)
The way it takes place is that first
Edit: The first part is explanation why it won't work
You get a list generated by the range function(Not since python3 though), now it only returns a function range and its current state, this can be of float or int, so using /10 as step there will do nothing other than Invalid syntax, because range only accepts integers.
Only after this does x gets initialized and starts taking values present in the dictionary in an indexed way.
You can however use
for y in range(2,-1,-1):
x = (10**y)
Welcome to python and happy coding.
Thanks to #GPhilo for pointing out the errors.

Related

Python list with zeros and numbers repeating

My intent is to generate a generic empty list and then to append it with a numeric sequence such that it gives zeros but at the third place it gives 3 and then its multiples, that is a[(0,0,3,0,0,6,0,0,9)] and it needs to have 100 values inside.
I first set the list and then I use the 'for' loop in a range(0,100) and I am sure I need to use % in ways such that whenever my sequence going 1 to 100 is perfectly divisible by 3 it gives back 3 (and not 0) but then it keeps evolving in 6,9,12.. How do I do?
for i in range(0,100):
if i%3==0:
return 0
else
return 3
Of course this is completely wrong but i am new to programming in general. Thanks in advance.
you could try this:
for i in range(0, 100, 3):
list[i]=i
You just change the "step" of the range function, so the index i will represent also the value passed in the list, it should work properly.
#Mark's comment is very relevant and makes good use of the modulo and list comprehension properties in a very simple way. Moreover, its code easily adapts to any value.
However the modulo operator is quite slow so here are other ways to achieve the same result.
Method 1
We can make a range from 3 to 101 with a step of 3, and add [0, 0, i] to each step. Since there will be missing zeros at the end of the list, we must add as many as the rest of the division of 100 by 3.
data = [num for i in range(3, 101, 3) for num in [0, 0, i]] + [0] * 1
Method 2
With the same idea, we can use .extend() to add two 0s before each term.
data = []
for i in range(3, 101, 3):
data.extend([0, 0, i])
data.append(0)
Method 3
The simplest idea, we create a list of 100 zeros, and then we modify the value every 3 terms.
data = [0] * 100
for i in range(2, 101, 3):
data[i] = i + 1
Comparison
Using timeit, here is a comparison of the speed of each algorithm, the comparison is based on 10000 repetitions.
import timeit
print(timeit.timeit("data = [0 if n % 3 else n for n in range(1, 101)]"))
print(timeit.timeit("data = [num for i in range(3, 101, 3) for num in [0, 0, i]] + [0] * 1"))
print(timeit.timeit("""
data = []
for i in range(3, 101, 3):
data.extend([0, 0, i])
data.append(0)
"""))
print(timeit.timeit("""
data = [0] * 100
for i in range(2, 101, 3):
data[i] = i + 1
"""))
Output:
4.137781305000317
3.8176420609997876
2.4403464719998738
1.4861199529996156
The last algorithm is thus the fastest, almost 3 times faster than using modulus.
A function stops running, when it encounters a return. So in your code, you only ever execute the loop once.
But, what if we could change that? Do you know what a generator is? Have a look at this:
def multiples_of_three_or_zero():
for i in range(0,100):
if i%3==0:
yield 0
else
yield 3
That's a generator. yield doesn't end the execution, but rather suspends it. You use it like this:
for i in multiples_of_three_or_zero():
print(i)
Or, if you really want all the elements in a list, just make a list from it:
list(multiples_of_three_or_zero())
Ok I practiced a little bit and I found this solution which best suits my needs (it was for a take-home exercise)
A=[]
for a in range (1,101):
if a%3==0:
A.append(a)
else:
A.append(0)
print(A)
thanks you all!

Iterate a huge list of integers which grow exponentially, where the exponent is 1.5

I need to iterate a list of integers:
2, 3, 4, 6, 9, 13, 19, 28, 42, ...
So the general rule is list[i+1] = list[i]*3/2.
The list should end at or right before 10^34.
This means that the last element is a number between 10^34*2/3 and 10^34.
I obviously cannot have this list preallocated in memory, so something like:
list = [2]
while True:
next = list[-1]*3/2
if next > 10**34:
break
list.append(next)
Is out of the question.
I can of course simply use the above in order to iterate these integers without having them stored in a list or generated by an iterator of some sort.
But the problem is that I have nested loops, like so:
for i in xrange(...):
for j in xrange(...):
for m in xrange(...):
for n in xrange(...):
So breaking this into several while loops would make the code pretty horrible.
Ideally, I would like to have some sort of xrange which generates this list of numbers "on the fly" (as xranges normally do).
As I mentioned in my comment, the list won't actually be very long. You could therefore initialise it as:
[int(2 * (1.5 ** n)) for n in range(int(math.log(10 ** 34, 1.5) - 1))]
However, this is actually slightly different to the example you gave, wherein you round to integer before generating the next number. In this case, you would have to do something iterative instead (as far as I can tell):
i = 2
lst = []
while i < 10 ** 34:
lst.append(i)
i = int(i * 1.5)
This might be what you're looking for:
lst = [2*1.5**i for i in range(0,192)]
The last term would be 2*1.5^191.
If you want them all to be integers you could say use the int() cast.
But you should note that the time/memory to do this will probably be similar to what you were doing in your example code. They are both doing similar things in the end.
If you want them all to be integers throughout the process:
i = 2
lst = [2]
while i < 10**34:
lst.append(int(i*=1.5))
Again, this will only save a trace amount of memory/time.

Accelerating for loops with list methods in Python

I have for loops in python that iterates nearly 2.5 million times and it's taking so much time to get a result. In JS I can make this happen in nearly 1 second but Python does it in 6 seconds on my computer. I must use Python in this case. Here is the code:
for i in xrange(k,p,2):
arx = process[i]
ary = process[i+1]
for j in xrange(7,-1,-1):
nx = arx + dirf[j]
ny = ary + dirg[j]
ind = ny*w+nx
if data[ind] == e[j]:
process[c]=nx
c=c+1
process[c]=ny
c=c+1
matrix[ind]=1
Here is some lists from code:
process = [None] * (11000*4) it's items will be replaced with integers after it's assignment.
dirf = [-1,0,1,-1,1,-1,0,1]
dirg = [1,1,1,0,0,-1,-1,-1]
e = [9, 8, 7, 6, 4, 3, 2, 1]
the data list is consists of 'r' informations from pixels of an rgba image.
data = imgobj.getdata(0)
How can I boost this. What am I doing wrong? Is there any other approaches about for loops? Thanks.
Here are a few suggestions for improving your code:
That inner xrange is being used a lot: what if you made that a regular list and just did something like this:
inner = range(7,-1,-1) # make the actual list
for(a,b,c): #first for
#stuff
for i in inner # reference an actual list and not the generator
Evidence :
n = range(7,-1,-1)
def one():
z = 0
for k in xrange(100):
for i in n:
z+=1
def two():
z = 0
for k in xrange(100):
for i in xrange(7,-1,-1):
z+=1
if __name__ == '__main__':
import timeit
print("one:")
print(timeit.timeit("one()",number=1000000 ,setup="from __main__ import one"))
print("two:")
print(timeit.timeit("two()",number=1000000 ,setup="from __main__ import two"))
"result"
one:
37.798637867
two:
63.5098838806
If the code I wrote is comparable, it would appear to indicate that referencing the inner list and not generating really speeds it up.
[edit] referencing local variable is faster than accessing global.
so if this is correct place the list definition as close to the loop as possible without having it generate every time.
You are also changing process twice. If it's not needed, just choose one.
As you mentioned in the comments, you say you're working with images. I am not sure if the following is relevant, but perhaps you could use openCV, which has a Python API to C code. That might speed it up. As others have mentioned: numpy and your own cython extensions will speed this up considerably.

Two value in two lists, simplify the code

I have two lists, and I want to compare the value in each list to see if the difference is in a certain range, and return the number of same value in each list. Here is my code 1st version:
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
k=0
for i in xrange(0, len(m)):
for j in xrange(0, len(n)):
if abs(m[i] - n[j]) <=0.5:
k+=1
else:
continue
the output is 3. I also tried 2nd version:
for i, j in zip(m,n):
if abs(i - j) <=0.5:
t+=1
else:
continue
the output is 1, the answer is wrong. So I am wondering if there is simpler and more efficient code for the 1st version, I have a big mount of data to deal with. Thank you!
The first thing you could do is remove the else: continue, since that doesn't add anything. Also, you can directly use for a in m to avoid iterating over a range and indexing.
If you wanted to write it more succiently, you could use itertools.
import itertools
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
k = sum(abs(a - b) <= 0.5 for a, b in itertools.product(m, n))
The runtime of this (and your solution) is O(m * n), where m and n are the lengths of the lists.
If you need a more efficient algorithm, you can use a sorted data structure like a binary tree or a sorted list to achieve better lookup.
import bisect
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
n.sort()
k = 0
for a in m:
i = bisect.bisect_left(n, a - 0.5)
j = bisect.bisect_right(n, a + 0.5)
k += j - i
The runtime is O((m + n) * log n). That's n * log n for sorting and m * log n for lookups. So you'd want to make n the shorter list.
More pythonic version of your first version:
ms = [1, 3, 5, 7]
ns = [1, 4, 7, 9, 5, 6, 34, 52]
k = 0
for m in ms:
for n in ns:
if abs(m - n) <= 0.5:
k += 1
I don't think it will run faster, but it's simpler (more readable).
It's simpler, and probably slightly faster, to simply iterate over the lists directly rather than to iterate over range objects to get index values. You already do this in your second version, but you're not constructing all possible pairs with that zip() call. Here's a modification of your first version:
m = [1,3,5,7]
n = [1,4,7,9,5,6,34,52]
k=0
for x in m:
for y in n:
if abs(x - y) <=0.5:
k+=1
You don't need the else: continue part, which does nothing at the end of a loop, so I left it out.
If you want to explore generator expressions to do this, you can use:
k = sum(sum( abs(x-y) <= 0.5 for y in n) for x in m)
That should run reasonably fast using just the core language and no imports.
Your two code snippets are doing two different things. The first one is comparing each element of n with each element of m, but the second one is only doing a pairwise comparison of corresponding elements of m and n, stopping when the shorter list runs out of elements. We can see exactly which elements are being compared in the second case by printing the zip:
>>> m = [1,3,5,7]
>>> n = [1,4,7,9,5,6,34,52]
>>> zip(m,n)
[(1, 1), (3, 4), (5, 7), (7, 9)]
pawelswiecki has posted a more Pythonic version of your first snippet. Generally, it's better to directly iterate over containers rather than using an indexed loop unless you actually need the index. And even then, it's more Pythonic to use enumerate() to generate the index than to use xrange(len(m)). Eg
>>> for i, v in enumerate(m):
... print i, v
...
0 1
1 3
2 5
3 7
A rule of thumb is that if you find yourself writing for i in xrange(len(m)), there's probably a better way to do it. :)
William Gaul has made a good suggestion: if your lists are sorted you can break out of the inner loop once the absolute difference gets bigger than your threshold of 0.5. However, Paul Draper's answer using bisect is my favourite. :)

How to count by twos with Python's 'range'

So imagine I want to go over a loop from 0 to 100, but skipping the odd numbers (so going "two by two").
for x in range(0,100):
if x%2 == 0:
print x
This fixes it. But imagine I want to do so jumping two numbers? And what about three? Isn't there a way?
Use the step argument (the last, optional):
for x in range(0, 100, 2):
print(x)
Note that if you actually want to keep the odd numbers, it becomes:
for x in range(1, 100, 2):
print(x)
Range is a very powerful feature.
(Applicable to Python <= 2.7.x only)
In some cases, if you don't want to allocate the memory to a list then you can simply use the xrange() function instead of the range() function. It will also produce the same results, but its implementation is a bit faster.
for x in xrange(0,100,2):
print x, #For printing in a line
>>> 0, 2, 4, ...., 98
Python 3 actually made range behave like xrange, which doesn't exist anymore.
for i in range(0, 100, 2):
print i
If you are using an IDE, it tells you syntax:
min, max, step(optional)

Categories