【问题标题】:Increase speed using only one loop?只使用一个循环来提高速度?
【发布时间】:2020-03-16 11:44:57
【问题描述】:

有没有办法在输入非常大的数字时仅使用一个循环来重写此代码以提高速度?

该代码用于计算列表中有多少整数大于其右侧的所有整数。

count = 0
for i,x in enumerate(items):
    d = True
    for y in items[i+1:]:
        if x <= y:
            d = False
    if d:
        count = count+1        
return count

【问题讨论】:

  • 列表预计有多长? 100 件、10000 件甚至数百万件?
  • 这不是问题的真正答案,但如果您在发现“假”值时中断第二个循环,则可以加快第二个循环。
  • @chepner 我将这个问题理解为计算有多少元素大于右侧的元素;在升序列表中,没有元素满足该条件(或者可能只是最后一个),但在降序列表中,每个元素都满足该条件。还是我误解了这个问题?

标签: python-3.x performance for-loop


【解决方案1】:

当前值大于右边的所有值当且仅大于最大值。

这段代码通过从右到左迭代来实现上述思想:

count = 0
max = None
for val in items[::-1]:
    if max is None or val > max:
        max = val
        count += 1
return count

【讨论】:

  • 如果你真的对&gt;而不是&gt;=感兴趣,则计数永远不会高于1。在[5, 5, 5] 列表中,只有最右边的5 将有助于计数。该函数为空输入返回 0,为非空输入返回 1,因此只需要长度,而不需要实际内容。
  • @chepner: 它可以大于 1,例如在这种情况下:[5, 4, 3, 2, 1] -- 每个数字都大于右边的所有数字。
  • @qwertyman 你的提议很好;我在my answer进行了比较
【解决方案2】:

我计时了一些选项来比较它们:

问题中的代码:

def f1(item_list):
    count = 0

    for i, x in enumerate(item_list):
        d = True
        for y in item_list[i+1:]:
            if x <= y:
                d = False
        if d:
            count = count+1

    return count

来自this answer from qwertyman的代码:

def f2(item_list):
    max_elem = None
    count = 0

    for val in item_list[::-1]:
        if max_elem is None or val > max_elem:
            max_elem = val
            count += 1

    return count

我的改进版(只是用reversed()而不是[::-1]):

def f3(item_list):
    max_elem = None
    count = 0

    for val in reversed(item_list):
        if max_elem is None or max_elem < val:
            max_elem = val
            count += 1

    return count

比较代码:

if __name__ == '__main__':
    func_list = [f1, f2, f3]
    print('{:>8s} {:15s} {:>10s} {:>10s} {:>10s}'.format(
        'n', 'items', 'f1', 'f2', 'f3'))

    for n in (100, 1000, 5000):
        items_1 = [random.randint(1, 1000) for _ in range(n)]
        items_2 = list(sorted(items_1))
        items_3 = list(sorted(items_1, reverse=True))

        for label, items in [
            ('random', items_1),
            ('sorted', items_2),
            ('sorted-reverse', items_3),
        ]:
            # assure that all functions return the same result
            assert len(set([func(items) for func in func_list])) == 1

            t_list = []
            for func in func_list:
                t_list.append(
                    timeit.timeit(
                        'func(items)',
                        'from __main__ import func, items',
                        number=100))
            print('{:8d} {:15s} {:10.6f} {:10.6f} {:10.6f}'.format(
                n, label, *t_list))

结果(在Ubuntu 18.04 上使用Python 3.6):

       n items                   f1         f2         f3
     100 random            0.016022   0.000348   0.000370
     100 sorted            0.015840   0.000339   0.000326
     100 sorted-reverse    0.014122   0.000572   0.000505
    1000 random            1.502731   0.003212   0.003077
    1000 sorted            1.496299   0.003332   0.003089
    1000 sorted-reverse    1.256896   0.005412   0.005196
    5000 random           36.812474   0.015695   0.014762
    5000 sorted           36.902378   0.015983   0.015067
    5000 sorted-reverse   31.218129   0.019741   0.018419

显然,qwertyman 的提议比原始代码快了几个数量级,并且可以通过使用 reversed() 加快一点速度(显然,为了更快的速度,可以使用另一种语言而不是 Python) .

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 2019-08-01
    • 2014-07-27
    • 1970-01-01
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    相关资源
    最近更新 更多