I need your help. What is the most efficient way of substituting multiple nested for loops (over 5 nested loops into each other - 5 in the code example) if they all go through the same range (let's say from 1 to 10) and after the last for loop, at the bottom, i append every sum of the iterated items to the list. The code looks smth like this :
b=[]
for i in range(1, 11):
for j in range(1, 11):
for k in range(1, 11):
for l in range(1, 11):
for m in range(1, 11):
b.append(i+j+k+l+m)
It is obviously not memory-friendly and will take some time. As far as i know, itertools can not really help here either. So how could it be helped?
use itertools.product()
from itertools import product
b = [sum(item) for item in product(range(1, 11), repeat=5)]
You may want to make b generator, so that it is evaluated lazily
b = (sum(item) for item in product(range(1, 11), repeat=5))
I recommend using recursive programming. This happens when a function calls itself to do the same task with different parameters.
def someIteration(b, i, j, k, l, m):
for i in range(1,11):
b.append(i+j+k+l+m)
if (Certain task done):
return b
else:
someIteration(b, i, j, k, l, m)
I dont think this function will work directly, I recommend to do some research in recursive programming. It's a really interesting paradigm, mostly used in ai!
pros:
its very code efficient
cons:
when the stack is full, it will crash (this rarely happens, most of the time due to a bug.)
Related
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!
The Problem:
Consider a d-dimensional simple cubic lattice.
If the lattice has width L, then the number of lattice sites is Ld. I want to create a list that contains all the positions of the lattice sites, for a general d and L.
For example, when L = 2 and d = 2 this would be [(0, 0), (1, 0), (0, 1), (1, 1)].
My Attempt:
Whilst I can do this for general L, I have not been able to generalise the dimension d.
Below is my solution for d = 3 using three for loops.
def Positions(L):
PositionList = []
for i in range(L):
for j in range(L):
for k in range(L):
PositionList.append([k, j, i])
return PositionList
It's easy to see how I would change it to increase or decrease the dimension d as I could simply add or remove the for loops, but obviously it is very tedious to write this out for large d.
I thought about using a recursive for loop, so I would only be using one for loop for any d, but I don't know how to do this whilst preserving the indexing I need to write out the positions.
In Conclusion:
Is it possible to have a variable number of for loops, each one having a distinct index to solve this problem?
Or is there a better solution without using for loops?
Recursion is indeed the way to go
The idea is:
If you assume your function works for d-1 dimensions, then you can take that result and append to each of the results the value of i (the loop variable), and do that repeatedly for each value of i.
The base case is when d=0, in that case you have just a single, empty result.
Here is how that can be coded:
def Positions(L, d):
if d == 0: # base case
return [[]]
return [
[i] + res # prepend i to the results coming from recursion
for i in range(L)
for res in Positions(L, d-1)
]
If you are not familiar with the list-comprehension syntax used in the final statement, then here is how you would do it without that syntax:
def Positions(L, d):
if d == 0: # base case
return [[]]
positions = []
for i in range(L):
for res in Positions(L, d-1):
positions.append([i] + res)
return positions
One easy way is using itertools cartesian product:
from itertools import product
L, D = 2, 2
print(list(product(list(range(L)), repeat = D)))
Result
[(0, 0), (0, 1), (1, 0), (1, 1)]
you use recursion. the first part is the base case and the second part is to add every number from 0 to L-1 for every term in the lattice for the lower dimension
def positions(L,d):
if d==0:
return [()]
else:
return [(x,)+positions(L,d-1)[y] for x in range(L) for y in range(len(positions(L,d-1)))]
hello I am relatively new to python! Is there a way to do this using for loops in python?
This is a java implementation of something i want to do in python
for (i=1;i<20; i*= 2)
{System.out.println(i);}
Solution in while loop in python`
while i<20:
print i
i*=2
I cannot figure out a way to do this using for loops. Implemented it using while loop obviously, but still curious to know whether there is a method to do so or not
There are lots of ways to do this, e.g.
for i in range(5):
i = 2 ** i
print i
or using generators
from itertools import count, takewhile
def powers_of_two():
for i in count():
yield 2 ** i
for i in takewhile(lambda x: x < 20, powers_of_two()):
print i
But in the end, it depends on your use case what version gives the clearest and most readbale code. In most cases, you would probably just use a while-loop, since it's simple and does the job.
You think of for loops like they would be in other languages, like C, C++, Java, JavaScript etc.
Python for loops are different; they work on iterables, and you always have to read them like:
for element in iterable
instead of the C'ish
for(start_condition; continue_condition; step_statement)
Hence, you would need iterable to generate your products.
I like readability, so here's how I'd do it:
for a in (2**i for i in range(20)):
print a
But that mainly works because we mathematically know that the i'th element of your sequence is going to be 2**i.
There is not a real way to do this in Python. If you wanted to mimic the logic of that for loop exactly, then a manual while loop would definitely be the way to go.
Otherwise, in Python, you would try to find a generator or generator expression that produces the values of i. Depending on the complexity of your post loop expression, this may require an actual function.
In your case, it’s a bit simpler because the numbers you are looking for are the following:
1 = 2 ** 0
2 = 2 ** 1
4 = 2 ** 2
8 = 2 ** 3
...
So you can generate the numbers using a generator expression (2 ** k for k in range(x)). The problem here is that you would need to specify a value x which happens to be math.floor(math.log2(20)) + 1 (because you are looking for the largest number k for which 2 ** k < 20 is true).
So the full expression would be this:
for i in (2 ** k for k in range(math.floor(math.log2(20)) + 1)):
print(i)
… which is a bit messy, so if you don’t necessarily need the i to be those values, you could move it inside the loop body:
for k in range(math.floor(math.log2(20)) + 1):
i = 2 ** k
print(i)
But this still only fits your purpose. If you wanted a “real” C-for loop expression, you could write a generator function:
def classicForLoop (init, stop, step):
i = init
while i < stop:
yield i
i = step(i)
Used like this:
for i in classicForLoop(1, 20, lambda x: x * 2):
print(i)
Of course, you could also modify the generator function to take lambdas as the first and second parameter, but it’s a bit simpler like this.
Use range() function to define iteration length.You can directly use print() than system.out.println
Alexander mentioned it and re-iterating
for i in range(1,20):print(i*2)
You can also consider while loop here-
i=0
while (i<20):
print(2**i)
i=i+1
Remember indentation in python
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. :)
I would like to implement the following snippet using just the inntermost for loop(the one which iterates 3 times),as this consumes a lot of time.
for i in arange(r):
for j in arange(c):
for k in arange(3):
if m[i,j]==n[i,j,k]:
new[i,j]=old[i,j,k]
Could anyone suggest a better method?
for k in range(3):
ind = m == n[:,:,k]
new[ind] = old[:,:,k][ind]
Look at using itertools.product - never used it with numpy arrays, but it might just work (and don't see why not)
for i, j, k in itertools.product(arange(r), arange(c), arange(3)):
if m[i,j]==n[i,j,k]:
new[i,j]=old[i,j,k]
Since arange(c) is computed for each i, and arange(3) for each couple (i, j), computing them once and for all outside the loop could save some time:
range_j = arange(c)
range_3 = arange(3)
for i in arange(r):
for j in range_j:
for k in range_3:
if m[i,j]==n[i,j,k]:
new[i,j]=old[i,j,k]
Of course this is only valid because these ranges are independent from i and j.