How to make this Python for-loop run faster? - python

for j in range(0, NumberOfFeatures):
for k in range(j+1,NumberOfFeatures):
countArray = np.ones((2,2))
for i in range(0,NumberOfTrainingExamples):
countArray[XTrain[i,j],XTrain[i,k]] += 1
The innermost for loop takes quite some time for large NumberOfFeatures, NumberOfTrainingExamples

Its O(n^3) basically (where n is not the same number).
Because the code is not complete it is very hard to determine what could be done better but from what you provided, try to reduce it to at least n^2 otherwise it will just take some time.
If you have 10 of each its 1000 cycles, 1000 of each is 1,000,000,000 so with bigger numbers it gets hard to calculate very fast.

Related

Differences bewteen time consumed of python loops

I am running a program and it really consumes a lot of time for a 3-layers loop. I reduced the size of loop and observed an interesting thing.
When the first layer is 50 iterations, it only comsumes 3 seconds. But when I changed it to 100 iterations, the time increased to 43 seconds. Why the time spent not doubled when the number of iterations just doubled? How the calculation complexity was calculted... I dont understand.
By the way my original designed loop was 160x192x160. It spent really a lot of time and I just stopped it. I think I need to figure out one way to solve this time problem. This is why I tried above mentioned.
start=time.time()
choice_list=[]
result_list=[]
mean_list=[]
point_info=[]
patch_radius=patch_radius
for k in range (0,50):
for l in range (0,100):
for h in range (0,10):
if img[k][l][h]!=0:
mean=patch_mean(coordinate_x=k,coordinate_y=l,coordinate_z=h,image=img,patch_radius=patch_radius)
point_info=[k,l,h,mean]
mean_list.append(point_info)
end=time.time()
print(end-start)
patch_mean is a function calculated the mean around a point. It is another loop. I think it would not matter. Because it is an independent fucntion. To be clear, patch raidus is a constant
def patch_mean(coordinate_x,coordinate_y,coordinate_z,image,patch_radius):
sum=0
count=0
for k in range(coordinate_x- patch_radius, coordinate_x + patch_radius):
for l in range(coordinate_y - patch_radius, coordinate_y + patch_radius):
for h in range (coordinate_z - patch_radius, coordinate_z + patch_radius):
if 0<k<159 and 0<l<191 and 0<h<159:
if img[k][l][h] != 0:
sum = sum + img[k][l][h]
count = count + 1
if count==0:
mean=0
else:
mean=sum/count
return mean
The first iterations of your outer loop give you coordinates that are near boundary of your image. That makes the patch_mean faster to calculate, as a big chunk of its area is cut off. When you move towards the middle of the image, the computation will be slower, since you'll be able to get an average of the whole patch area, not just a part of it.
If you change the range from range(0, 50) to range(0, 100), you're will be a lot more of the middle part of the image. Those coordinates are the slow ones, so overall, the loop will be a lot slower. If you changed it to range(0, 160), you'd find that the last few iterations would speed up again, as you'd start running into the other side of the image. But the interval from 50-100 is right in the middle of the image, and will be the slowest part.

What is optimal algorithm to check if a given integer is equal to sum of two elements of an int array?

def check_set(S, k):
S2 = k - S
set_from_S2=set(S2.flatten())
for x in S:
if(x in set_from_S2):
return True
return False
I have a given integer k. I want to check if k is equal to sum of two element of array S.
S = np.array([1,2,3,4])
k = 8
It should return False in this case because there are no two elements of S having sum of 8. The above code work like 8 = 4 + 4 so it returned True
I can't find an algorithm to solve this problem with complexity of O(n).
Can someone help me?
You have to account for multiple instances of the same item, so set is not good choice here.
Instead you can exploit dictionary with value_field = number_of_keys (as variant - from collections import Counter)
A = [3,1,2,3,4]
Cntr = {}
for x in A:
if x in Cntr:
Cntr[x] += 1
else:
Cntr[x] = 1
#k = 11
k = 8
ans = False
for x in A:
if (k-x) in Cntr:
if k == 2 * x:
if Cntr[k-x] > 1:
ans = True
break
else:
ans = True
break
print(ans)
Returns True for k=5,6 (I added one more 3) and False for k=8,11
Adding onto MBo's answer.
"Optimal" can be an ambiguous term in terms of algorithmics, as there is often a compromise between how fast the algorithm runs and how memory-efficient it is. Sometimes we may also be interested in either worst-case resource consumption or in average resource consumption. We'll loop at worst-case here because it's simpler and roughly equivalent to average in our scenario.
Let's call n the length of our array, and let's consider 3 examples.
Example 1
We start with a very naive algorithm for our problem, with two nested loops that iterate over the array, and check for every two items of different indices if they sum to the target number.
Time complexity: worst-case scenario (where the answer is False or where it's True but that we find it on the last pair of items we check) has n^2 loop iterations. If you're familiar with the big-O notation, we'll say the algorithm's time complexity is O(n^2), which basically means that in terms of our input size n, the time it takes to solve the algorithm grows more or less like n^2 with multiplicative factor (well, technically the notation means "at most like n^2 with a multiplicative factor, but it's a generalized abuse of language to use it as "more or less like" instead).
Space complexity (memory consumption): we only store an array, plus a fixed set of objects whose sizes do not depend on n (everything Python needs to run, the call stack, maybe two iterators and/or some temporary variables). The part of the memory consumption that grows with n is therefore just the size of the array, which is n times the amount of memory required to store an integer in an array (let's call that sizeof(int)).
Conclusion: Time is O(n^2), Memory is n*sizeof(int) (+O(1), that is, up to an additional constant factor, which doesn't matter to us, and which we'll ignore from now on).
Example 2
Let's consider the algorithm in MBo's answer.
Time complexity: much, much better than in Example 1. We start by creating a dictionary. This is done in a loop over n. Setting keys in a dictionary is a constant-time operation in proper conditions, so that the time taken by each step of that first loop does not depend on n. Therefore, for now we've used O(n) in terms of time complexity. Now we only have one remaining loop over n. The time spent accessing elements our dictionary is independent of n, so once again, the total complexity is O(n). Combining our two loops together, since they both grow like n up to a multiplicative factor, so does their sum (up to a different multiplicative factor). Total: O(n).
Memory: Basically the same as before, plus a dictionary of n elements. For the sake of simplicity, let's consider that these elements are integers (we could have used booleans), and forget about some of the aspects of dictionaries to only count the size used to store the keys and the values. There are n integer keys and n integer values to store, which uses 2*n*sizeof(int) in terms of memory. Add to that what we had before and we have a total of 3*n*sizeof(int).
Conclusion: Time is O(n), Memory is 3*n*sizeof(int). The algorithm is considerably faster when n grows, but uses three times more memory than example 1. In some weird scenarios where almost no memory is available (embedded systems maybe), this 3*n*sizeof(int) might simply be too much, and you might not be able to use this algorithm (admittedly, it's probably never going to be a real issue).
Example 3
Can we find a trade-off between Example 1 and Example 2?
One way to do that is to replicate the same kind of nested loop structure as in Example 1, but with some pre-processing to replace the inner loop with something faster. To do that, we sort the initial array, in place. Done with well-chosen algorithms, this has a time-complexity of O(n*log(n)) and negligible memory usage.
Once we have sorted our array, we write our outer loop (which is a regular loop over the whole array), and then inside that outer loop, use dichotomy to search for the number we're missing to reach our target k. This dichotomy approach would have a memory consumption of O(log(n)), and its time complexity would be O(log(n)) as well.
Time complexity: The pre-processing sort is O(n*log(n)). Then in the main part of the algorithm, we have n calls to our O(log(n)) dichotomy search, which totals to O(n*log(n)). So, overall, O(n*log(n)).
Memory: Ignoring the constant parts, we have the memory for our array (n*sizeof(int)) plus the memory for our call stack in the dichotomy search (O(log(n))). Total: n*sizeof(int) + O(log(n)).
Conclusion: Time is O(n*log(n)), Memory is n*sizeof(int) + O(log(n)). Memory is almost as small as in Example 1. Time complexity is slightly more than in Example 2. In scenarios where the Example 2 cannot be used because we lack memory, the next best thing in terms of speed would realistically be Example 3, which is almost as fast as Example 2 and probably has enough room to run if the very slow Example 1 does.
Overall conclusion
This answer was just to show that "optimal" is context-dependent in algorithmics. It's very unlikely that in this particular example, one would choose to implement Example 3. In general, you'd see either Example 1 if n is so small that one would choose whatever is simplest to design and fastest to code, or Example 2 if n is a bit larger and we want speed. But if you look at the wikipedia page I linked for sorting algorithms, you'll see that none of them is best at everything. They all have scenarios where they could be replaced with something better.

Append Multiples of Squares of integers greater than 2 and sort them

For Example :
4,8,9,12,16,18,.......
How can we do this for big size array?
The basic one I made, took a lot of time to execute :
for i in range(1,1000):
for j in range(2,1000):
l.append((i)*((j)*(j)))
s=set(l)
l1=list(s)
l1.sort()
print(l1)
Size of list should be in order of 10^6.
I wrote your code in a more better way:
my_list = sorted([i*(j**2) for i in range(1, 1000) for j in range(2, 1000)])
However, what you are asking is execution time which for this operation takes a fraction of seconds (around 1.5 second) and what you are seeing is printing time which in different editors and interfaces is different. For example I ran this script in sublime text it took almost 9 minutes while it took 1.4 minute in command line.
if you check the length of your list:
print len(sorted([i*(j**2) for i in range(1, 1000) for j in range(2, 1000)]))
you will see, to print a list with 997002 elements, obviously you should consider an execution time.
Another factor which you should take into account is the Big-O Notation which in your case called O(n^2) notation and this run time will exponentially increase if you increase the number of loops.

Decreasing while loop working time for gigantic numbers

i = 1
d = 1
e = 20199
a = 10242248284272
while(True):
print(i)
if((i*e)%a == 1):
d = i
break
i = i + 1
Numbers are given to represent lengths of them.
I have a piece of Python code which works for really huge numbers and I have to wait for a day maybe two. How can I decrease the time needed for the code to run?
There are a variety of things you can do to speed this up; whether it will be enough is another matter.
Avoid printing things you don't have to.
Avoid multiplication. Instead of computing i*e each iteration, you could have a new variable, say i_e, which holds this value. Each time you increment i, you can add e to it.
The vast majority of your iterations aren't even close to satisfying (i*e)%a==1. If you used a larger increment for i, you could avoid testing most of them. Of course, you have to be careful not to overshoot.
Because you are doing modular arithmetic, you don't need to use the full value of i in your test (though you will still need to track it, for the output)
Combining the previous two points, you can avoid computing the modulus: If you know that a<=x<2*a, then x-a==1 is equivalent to x%a==1.
None of these may be enough to reduce the complexity of the problem, but might be enough to reduce the constant factor to speed things up enough for your purposes.

Time and Space Complexity Trouble

I've seen so many time complexity problems but none seem to aid in my understanding of it - like really get it.
What I have taken from my readings and attempts at practices all seems to come down to what was mentioned here Determining complexity for recursive functions (Big O notation) in the answer coder gave - which in fact did help me understand a little more about what's going on for time complexity.
What about a function that such as this:
def f(n):
if n < 3:
return n
if n >= 3:
return f(n-1) + 2*f(n-2) + 3*f(n-3)
Since the function calls the function 3 times, does that mean that the time complexity is O(3^n)?
As for the space complexity, it seems to be linear hence I propose the complexity to be O(n).
Am I wrong about this?
Since the function calls the function 3 times
This isn't really correct, but rather lets use examples that are more exact that your ad-hoc example.
def constant(n):
return n*12301230
This will always run in the same amount of time and is therefore O(1)
def linear(n):
total = 0
for x in xrange(n):
total+=1
return total
This has O(N) time
def quadratic(n):
total = 0
for x in xrange(n):
for y in xrange(n):
total+=1
return total
This runs in quadratic time O(N^2) since the inner loop runs n times and the outer loop runs n times.
There are also more specific examples for log(N), N*log(N), (2^N), etc but, going back to your question:
Since the function calls the function 3 times, does that mean that the time complexity is O(3^n)?
If the function is called 3 times, it will still be constant time for constant(x), linear for linear(x) and quadratic for quadratic(x). Importantly, O(3^n) is exponential time and is not the same as n^3. Then, we would not use a 3 as the base but a 2^n as a standard.
So your function will have a constant time for x<3. Best approximation to what your function gives, I'd run it through a timer but its recursive and difficult to compute. If you provide another, non-recursive example I'll be happy to tell you its complexity.
Hope this helps a bit, the graph doesn't do justice to how much faster 2^n grows in comparison to n^2 but it's a good start.

Categories

Resources