My question is quite similar to this one here:
Function with varying number of For Loops (python)
However, what I really want is for example:
def loop_rec(n):
for i in range(n):
for j in range(n):
for k in range(n):
#... n loops
#Do something with i, j and k
#Such as i+j+k
The example in the link does not allow the index x to vary.
Something like the answer suggested in that question but to use the indices instead of just x.
def loop_rec(y, n):
if n >= 1:
for x in range(y): # Not just x
loop_rec(y, n - 1)
else:
whatever()
Thanks
For problems where you have to deal with multiple nested loops, python standard library provides a convenient tool called itertools.product
In your particular case, all you have to do is to wrap the range with itertools.product, and specify how many nested loops via the repeat parameter. itertools.product, necessarily performs Cartesian product.
def loop_rec(y, n):
from itertools import product
for elems in product(range(y),repeat=n):
# sum(args)
# x,y,z ....(n) terms = elems
# index elems as elems[0], elems[1], ....
based on your requirement, you might want to use the entire tuple of each Cartesian product, or might want to index the tuple individually, or if you know the loop depth, you can assign it to the variables.
Thanks, but what if I wanted to change the range for each loop, ie. i
in range(n), j in range(n-1), k in range(n-2)
Assuming, you want to vary the range from m to n i.e. range(n), range(n-1), range(n-2), .... range(m). you can rewrite the product as
product(*map(range, range(m,n)))
.
Related
For example, in a situation where I have 100 different locations, I want to find the farthest location from the others. so I thought of a for loop that would take the difference of each position from the other 99 and add it to a list. the same locations in the loop should not meet.
I thought of making a condition for it. it works but I'm not sure if it's the right approach. What approach should I take in such a situation?
def getDifferenceCoord(t1, t2):
expDist = (t1[0] - t2[0]) ** 2 + (t2[1] - t1[1]) ** 2
dist = expDist ** 0.5
return dist
allDistance = []
for i in range(len(locationList)):
for j in range(len(locationList) - 1):
if i == j:
j = 99
allDistance.append(getDifferenceCoord(locationList[i], locationList[j]))
This will give you all the distances:
from itertools import permutations
allDistance = [getDifferenceCoord(i, j) for (i, j) in permutations(locationList, 2)]
If the return distances are the same (which they normally would be) a more efficient method is:
from itertools import combinations
allDistance = [getDifferenceCoord(i, j) for (i, j) in combinations(locationList, 2)]
permutations: all i,j where i!=j
combinations: all i,j where j>i (half of the permutations)
I think you have the right approach. There are minor optimisations that can be made for memory or processing complexity but it remains and O(N^2) problem either way.
One minor adjustment, if i == j, you should let the calculation equal 0, instead of putting j to the 99 to exit the loop. Unless you do want to break out of the loop in which case you should use the break command instead.
A fun addition: this is a possible solution using list comprehension.
alldistance = [
sum([getDifferenceCoord(locationList[i], locationList[j]) for j in range(0,99)])
for i in range(0, 99)
]
Try - >
import math
from itertools import combinations
for item in combinations(locationList,2):
print(f'distance for locations {item} = {math.dist(item[0], item[1])}')
You can use math.dist to evaluate the distance more cleanly -
import math
from itertools import combinations
result = [math.dist(item[0],item[1]) for item in combinations(locationList,2)]
def function_1(arr):
return [j for i in range(len(arr)) for j in range(len(arr))
if np.array(arr)[i] == np.sort(arr)[::-1][j]]
An arrarr array is given. It is required for each position [i] to find the arriarri element number in the arrarr array, sorted in descending order. All values of the arrarr array are different.
I have to write func in 1 line. It is working, but very slowly. I have to do this:
np.random.seed(42)
arr = function_1(np.random.uniform(size=1000000))
print(arr[7] + arr[42] + arr[445677] + arr[53422])
Please help to optimize the code.
You are repeatedly sorting and reversing the array, but the result of that operation is independent of the current value of i or j. The simple thing to do is to pre-compute that, then use its value in the list comprehension.
For that matter, range(len(arr)) can also be computed once.
Finally, arr is already an array; you don't need to make a copy each time through the i loop.
def function_1(arr):
arr_sr = np.sort(arr)[::-1]
r = range(len(arr))
return [j for i in r for j in r if arr[i] == arr_sr[j]]
Fitting this into a single line becomes trickier. Aside from extremely artificial outside constraints, there is no reason to do so, but once Python 3.8 is released, assignment expressions will make it simpler to do so. I think the following would be equivalent.
def function_1(arr):
return [j for i in (r:=range(len(arr))) for j in r if arr[i] == (arr_sr:=np.sort(arr)[::-1])[j]]
Have a think about the steps that are going on in here:
[j
for i in range(len(arr))
for j in range(len(arr))
if np.array(arr)[i] == np.sort(arr)[::-1][j]
]
Suppose your array contains N elements.
You pick an i, N different times
You pick a j N different times
Then for each (i,j) pair you are doing the final line.
That is, you're doing the final line N^2 times.
But in that final line, you're sorting an array containing N elements. That's an NlogN operation. So the complexity of your code is O(N^3.logN).
Try making a sorted copy of the array before your [... for i ... for j ...] is called. That'll reduce the time complexity to O(N^2 + NlogN)
I think...
Consider the following code:
for i in range(size-1):
for j in range(i+1,size):
print((i,j))
I need to go through this for-loop in a random fashion. I attempt to write a generator to do such a thing
def Neighborhood(size):
for i in shuffle(range(size-1)):
for j in shuffle(range(i+1), size):
yield i, j
for i,j in Neighborhood(size):
print((i,j))
However, shuffle cannot be applied to whatever object range is. I do not know how to remedy the situation, and any help is much appreciated. I would prefer a solution avoid converting range to a list, since I need speed. For example, size could be on the order of 30,000 and i will do perform this for loop around 30,000 times.
I also plan to escape the for loop early, so I want to avoid solutions that incorporate shuffle(list(range(size)))
You can use random.sample.
The advantage of using random.sample over random.shuffle, is , it can work on iterators, so in :
Python 3.X you don't need to convert range() to list
In Python 2,X, you can use xrange
Same Code can work in Python 2.X and 3.X
Sample code :
n=10
l1=range(n)
for i in sample(l1,len(l1)):
l2=range(i,n)
for j in sample(l2,len(l2)):
print(i,j)
Edit :
As to why I put in this edit, go through the comments.
def Neighborhood(size):
range1 = range(size-1)
for i in sample(range1, len(range1)):
range2 = range(i+1)
for j in sample(range2, len(range2)):
yield i, j
A simple way to go really random, not row-by-row:
def Neighborhood(size):
yielded = set()
while True:
i = random.randrange(size)
j = random.randrange(size)
if i < j and (i, j) not in yielded:
yield i, j
yielded.add((i, j))
Demo:
for i, j in Neighborhood(30000):
print(i, j)
Prints something like:
2045 5990
224 5588
1577 16076
11498 15640
15219 28006
8066 10142
7856 8248
17830 26616
...
Note: I assume you're indeed going to "escape the for loop early". Then this won't have problems with slowing down due to pairs being produced repeatedly.
I don't think you can randomly traverse an Iterator. You can predefine the shuffled lists, though
random iteration in Python
L1 = list(range(size-1))
random.shuffle(L1)
for i in L1:
L2 = list(range(i+1, size))
random.shuffle(L2)
for j in L2:
print((i,j))
Of course, not optimal for large lists
So, let's say I have an arbitrarily long list of numbers. I'd like to get a list of every number in that list multiplied by every number in that list. I'd do that by nesting for loops like this:
for x in numbers:
for y in numbers:
print(x*y)
Now if I'd like to multiply every number in that list times every number in that list times every number in that list again, I'd do this:
for x in numbers:
for y in numbers:
for z in numbers:
print(x*y*z)
My issue is that I'm searching a graph for a subgraph, and I need to allow for arbitrarily large subgraphs. To do this I have to construct every subgraph with n edges from the edges in the main graph - and I have to allow for arbitrary values of n. How?
itertools.product with an iterative product computing function (I favor reduce(mul, ...)). If you need n-way products (in both senses of the word "product"):
from functools import reduce
from operator import mul
for numset in itertools.product(numbers, repeat=n):
print(reduce(mul, numset))
The above is simple, but it will needlessly recompute partial products when the set of values is large and n >= 3. A recursive function could be used to avoid that:
def compute_products(numbers, repeat):
if repeat == 1:
yield from numbers
return
numbers = tuple(numbers) # Only needed if you want to handle iterator/generator inputs
for prod in compute_products(numbers, repeat-1):
yield from (prod * x for x in numbers)
for prod in compute_products(numbers, n):
print(prod)
I think a recursive function of some kind would probably help you. This is just untested pseudocode typed into the Stack Overflow editor, so beware of using this verbatim, but something like this:
def one_dimension(numbers, n):
if (n < 1):
return
for num in numbers:
for next_num in one_dimension(numbers, n-1):
yield num*next_num
The basic idea is that each "level" of the function calls the next one, and then deals with what the next one produces. I don't know how familiar you are with Python's yield keyword, but if you know what it does, then you should be able to adapt the code above.
Update: Or just use itertools.product like ShadowRanger suggested in his answer; that's probably a better solution to your problem.
How can I make this function faster? (I call it a lot of times and it could result in some speed improvements)
def vectorr(I, J, K):
vect = []
for k in range(0, K):
for j in range(0, J):
for i in range(0, I):
vect.append([i, j, k])
return vect
You can try to take a look at itertools.product
Equivalent to nested for-loops in a generator expression. For example,
product(A, B) returns the same as ((x,y) for x in A for y in B).
The nested loops cycle like an odometer with the rightmost element
advancing on every iteration. This pattern creates a lexicographic
ordering so that if the input’s iterables are sorted, the product
tuples are emitted in sorted order.
Also no need in 0 while calling range(0, I) and etc - use just range(I)
So in your case it can be:
import itertools
def vectorr(I, J, K):
return itertools.product(range(K), range(J), range(I))
You said you want it to be faster. Let's use NumPy!
import numpy as np
def vectorr(I, J, K):
arr = np.empty((I*J*K, 3), int)
arr[:,0] = np.tile(np.arange(I), J*K)
arr[:,1] = np.tile(np.repeat(np.arange(J), I), K)
arr[:,2] = np.repeat(np.arange(K), I*J)
return arr
There may be even more elegant tweaks possible here, but that's a basic tiling that gives the same result (but as a 2D array rather than a list of lists). The code for this is all implemented in C, so it's very, very fast--this may be important if the input values may get somewhat large.
The other answers are more thorough and, in this specific case at least, better, but in general, if you're using Python 2, and for large values of I, J, or K, use xrange() instead of range(). xrange gives a generator-like object, instead of constructing a list, so you don't have to allocate memory for the entire list.
In Python 3, range works like Python 2's xrange.
import numpy
def vectorr(I,J,K):
val = numpy.indices( (I,J,K))
val.shape = (3,-1)
return val.transpose() # or val.transpose().tolist()