【问题标题】:Which is the fastest way to calculate if elements of my list are increasing? [closed]如果我的列表中的元素正在增加,哪种计算方法是最快的? [关闭]
【发布时间】:2021-05-24 19:58:29
【问题描述】:

我有这个简单的代码来检查我的列表中的下一个元素是否比上一个大:

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)

这需要每秒运行多次,这些值以 FIFO 顺序不断移动(首先删除,移动所有队列并最后添加),并且此列表中始终有 5 个浮点数。

问题是:有没有更快的方法来执行此检查?

【问题讨论】:

  • 列表总是5个元素?
  • “每秒多次”实际上很慢。
  • 你怎么知道这太慢了?
  • 请发帖minimal reproducible example。看起来你是在暗示总是有五个元素......
  • AFAIK,如果列表中只有 5 个元素,您提供的方法可能是最快的方法

标签: python


【解决方案1】:

您可以使用zip() 函数和all() 函数:

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

currentList[:-1] 对原始列表进行切片,以便第一个切片排除最后一个元素。 currentList[1:] 做同样的事情,第一个元素。

zip() 这两个切片为我们提供了一个迭代器,它在解包时将元素 x 放入变量 i,并将元素 x+1 放入变量 j

然后,我们只需比较 ij 并检查所有此类对的条件是否成立。

由于需要对列表进行切片,这可能并不比写出所有内容更快,但是可以扩展到更长的列表而无需写出所有内容。切片的替代方法是在范围内迭代。

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

要检查哪一个最快,让我们将所有这些放在各自的函数中:

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)

显然,您的原始方法 (fun1) 是最快的(因为它不需要创建任何额外的对象),但是如果您的列表中有更多元素,那么编写起来很快就会很烦人。这个例子很好地说明了为什么“most pythonic”不是“fastest”的同义词。

【讨论】:

    【解决方案2】:

    这只是基于我从您的问题中能够理解的答案。如果这不是您想要的,请说明并详细说明。

    您可以对列表进行排序,然后查看列表是否相同:

    if currentList == sorted(currentList):
    

    或者,您可以遍历列表以查看元素是否更大

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

    【讨论】:

    • 如果列表已经排序,由于调用函数的开销,排序会稍微慢一些。如果列表排序,这可能会非常慢,因为即使第一对出现故障,您也会花费 O(n lg n) 时间。
    • 你需要len(currentList) - 1 否则你会在最后一次迭代中得到IndexError
    • @gold_cy 哦,谢谢!我在写这篇文章时混淆了 if 和 range 语句的逻辑......哎呀
    【解决方案3】:

    您可以通过将原始列表与排序列表进行比较来做到这一点:

    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 在 cmets 中提出了一个很好的观点,即对未排序列表进行排序是 O(n lg n),因此效率低于简单地遍历列表并执行相邻元素的成对比较,即 O( n)。您可以通过在检测到排序顺序违规时尽早停止迭代来进一步提高性能,如下所示:

    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'
    

    【讨论】:

    • 如果列表不按顺序排序是 O(n lg n),与简单地检查每对相邻元素相比,这个过程要慢得多。
    • @chepner。谢谢你。请参阅我修改后的答案。
    【解决方案4】:

    如果您是按先进先出顺序移动项目,您只需检查最后一个项目是否小于新项目:

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

    由于您一直在检查这一点,因此您不需要一直处理整个列表。这样做,比任何其他方法快 N 倍。此操作的复杂度为 O(1)。

    【讨论】:

    • 很遗憾没有,如果会有 [1,2,3,4,1] 那么对于这一次和接下来的 3 次迭代,我需要返回否定响应,因为整组数据不会不断增加
    • @Kodoj 我假设您想在您的列表不再增加时停止。我的错。
    猜你喜欢
    • 2012-04-02
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    • 2021-11-10
    • 2013-07-02
    • 2016-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多