1

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?

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
Kodoj
  • 13
  • 4
  • The list is always 5 elements? – juanpa.arrivillaga May 24 '21 at 20:00
  • 2
    "Multiple times per second" is actually pretty slow. – Scott Hunter May 24 '21 at 20:00
  • 4
    How do you know that this is too slow? – mkrieger1 May 24 '21 at 20:00
  • Please post an [MCVE]. It looks like you're implying that there are always five elements... – Mad Physicist May 24 '21 at 20:00
  • I thikn in terms of processing time this problem is pretty trivial. Especially, if you really have only a handful of conditions. But I would say you could improve this validation by using a loop instead. This way it will still work, when the list gets longer. – schilli May 24 '21 at 20:04
  • Question edited, yes, there is always 5 elements inside this list. – Kodoj May 24 '21 at 20:05
  • 1
    AFAIK, if there is only 5 elements in the list, the method you provided would probably be the fastest way – 12944qwerty May 24 '21 at 20:07
  • Your invariant is that the list is sorted. Removing the first element of the list cannot break the invariant; only adding a new smaller item to the end can do that. All you need to do is verify that each new item is greater than the end item. (Or if you can defer the check, check that the item removed is less than the new first item.) – chepner May 24 '21 at 20:10
  • What will be *slow* is removing the first element of the list; be sure you are using `collections.deque` to overcome that problem. – chepner May 24 '21 at 20:11
  • Does this answer your question? [Python - How to check list monotonicity](https://stackoverflow.com/q/4983258/6045800) – Tomerikoo May 24 '21 at 20:15
  • Thanks @Tomerikoo accepted answer from that question looks very nice. – Kodoj May 24 '21 at 20:49
  • Just check the last two values of the list and set one to the prior value, if the new value is ever smaller throw an exception. list[-1] then compare to list[-2], should be pretty fast. – Steven Barnard May 24 '21 at 21:08

4 Answers4

0

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")
12944qwerty
  • 2,001
  • 1
  • 10
  • 30
  • 2
    Sorting the list will be slightly slower if the list is already sorted, due to the overhead of calling the function. If the list is *not* sorted, this can be *much* slower, because you'll spend O(n lg n) time even if the very first pair is out of order. – chepner May 24 '21 at 20:04
  • you need to do `len(currentList) - 1` otherwise you will get an `IndexError` on the final iteration – gold_cy May 24 '21 at 20:13
  • @gold_cy Oh, thank you for that! I had mixed up the if and range statement's logic while writing this... oops – 12944qwerty May 24 '21 at 20:17
0

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'
pakpe
  • 5,391
  • 2
  • 8
  • 23
  • 2
    Sorting is O(n lg n) if the list is not in order, a significantly slower process compared to simply checking each adjacent pair of elements. – chepner May 24 '21 at 20:05
  • @chepner. Thank you. See my amended answer. – pakpe May 24 '21 at 20:55
0

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".

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
0

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).

OSainz
  • 522
  • 3
  • 6
  • unfortunately no, if there will be [1,2,3,4,1] then for this one and next 3 iterations I need to return negative response as whole set of data is not constantly increasing – Kodoj May 24 '21 at 21:33
  • @Kodoj I assumed you would like to stop when your list do not increase anymore. My bad. – OSainz May 24 '21 at 21:37