Which is the fastest way to calculate if elements of my list are increasing?

Question:

I have this simple code which checks if the next element of my list is bigger than previous:

if (currentList[0] < currentList[1]) and (currentList[1] < currentList[2]) and (currentList[2] < currentList[3]) and (currentList[3] < currentList[4]):
    print("everything is fine")
else:
    print("this is not fine)

This needs to run multiple times per second, those values are constantly moving in FIFO order (removing first, moving all queue and adding last) and there is always 5 floats inside this list.

Question is: is there any faster way to perform this check?

Asked By: Kodoj

||

Answers:

This is just an answer based on what I am able to understand from your question. Please specify and elaborate if this isn’t what you want.

You can sort the list and see if the list is the same afterwards:

if currentList == sorted(currentList):

OR you can loop through the list to see if an element isn’t greater

for i in range(0, len(currentList)-1):
    if currentList[i] > currentList[i+1]:
        break # Only occurs if the element is less then the next element
else: # Only occurs if break was called
    print("This is not fine")
Answered By: 12944qwerty

You can do it by comparing the original list to a sorted list:

def is_ascending(lst):
    if lst == sorted(lst):
        return 'OK'
    else:
        return 'NOT OK'

print(is_ascending([2,4,3,6])) #NOT OK
print(is_ascending([2,4,10,12])) #OK

Chepner raised a good point in the comments that sorting an unsorted list is O(n lg n) and, therefore, less efficient than simply iterating through the list and performing a pair-wise comparison of adjacent elements, which is O(n). You can further improve performance by stopping the iteration early as soon as a violation in the sort order is detected, as below:

def is_ascending(lst):
    for i in range(len(lst)-1):
        if lst[i+1] < lst[i]: #sort order violated. Stop iteration.
            return 'NOT OK'
    return 'OK'
Answered By: pakpe

You can use the zip() function and the all() function:

is_ascending = all(i < j for i, j in zip(currentList[:-1], currentList[1:]))

currentList[:-1] slices the original list so that the first slice excludes the last element. currentList[1:] does the same, with the first element.

zip() these two slices gives us an iterator that, when unpacked, puts element x in the variable i, and element x+1 in the variable j.

Then, we simply compare i and j and check that the condition holds for all such pairs.

This is probably not faster than writing everything out because of the need to slice the lists, but is extendable to longer lists without having to write everything out. An alternative to slicing is iterating over the range.

is_ascending = True
for i in range(len(currentList)-1):
    if currentList[i] >= currentList[i+1]: 
        is_ascending = False
        break

To check which one is fastest, let’s put all these in their own functions:

def fun1(currentList):
    return (currentList[0] < currentList[1]) and (currentList[1] < currentList[2]) and (currentList[2] < currentList[3]) and (currentList[3] < currentList[4])

def fun2(currentList):
    return all(i < j for i, j in zip(currentList[:-1], currentList[1:]))

def fun3(currentList):
    for i in range(len(currentList)-1):
        if currentList[i] >= currentList[i+1]: return False
    return True


testlist = [1, 2, 3, 4, 5]

%timeit fun1(testlist)
306 ns ± 29.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit fun2(testlist)
1.15 µs ± 44.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)


%timeit fun3(testlist)
741 ns ± 43.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Clearly, your original approach (fun1) is the fastest (because it doesn’t need to create any extra objects), but if you had more elements in your list it would quickly get annoying to write. This example is a good illustration of why "most pythonic" is not synonymous with "fastest".

Answered By: pho

If you are moving the items in FIFO order you only need to check whether the last item is smaller than the new item:

if current_list[-2] < current_list[-1]:
    print("everything is okay")
else:
    print("this is not fine")

As you are always cheking that, you dont need to proccess the whole list all the times. Doing this way, is N times faster that any other approach. The complexity of this operation is O(1).

Answered By: OSainz
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.