复杂列表理解比直接循环慢

时间:2017-04-02 09:25:38

标签: python

我在leetcode上看到了一个有趣的编程练习解决方案。它甚至没有解决问题/解决方案本身,所以如果您愿意,可以在提供的链接上阅读。然而,这是一个很高的投票解决方案:

Snippet 1

def fizzBuzz2(n):
    return  ["Fizz" * (not i%3) + "Buzz" * (not i%5) or str(i) for i in range(1, n+1)]

Snippet 2

def fizzBuzz(n):
    out = []
    for i in range(1, n+1):
        if i % 3 == 0 and i % 5 == 0:
            out.append("FizzBuzz")
        elif i % 3 == 0:
            out.append("Fizz")
        elif i % 5 == 0:
            out.append("Buzz")
        else:
            out.append(str(i))

    return out

但是,我预计列表理解会打败常见的循环,但是当我计时它并且情况并非如此。甚至做了一个dis,Snippet 2有更多指令。

什么使 Snippet 1 变慢?

4 个答案:

答案 0 :(得分:3)

你的代码段

def fizzBuzz2(n):
    return  ["Fizz" * (not i%3) + "Buzz" * (not i%5) or str(i) for i in range(1, n+1)]

执行大量字符串连接(即使是空字符串)。我通过一个额外的模运算来交换它,它保存了连接,并且它已经更快了。

def fizzBuzz3(n):
    return  ["FizzBuzz" if not i%15 else "Fizz" if not i%3 else "Buzz" if not i%5 else str(i) for i in range(1, n+1)]
在我的机器上运行时,两种理解都比“经典”方法更快,所以我得到的结果与你说的不同:

your comp: 4.927702903747559
my listcomp: 4.343341112136841
classical: 6.015967845916748

所以我的优化listcomp获胜(并且似乎也在你的机器上获胜),即使我对listcomp的流量控制引起的额外模运算不满意。

我的测试协议使用n=1000执行10000次操作:

import time
start_time = time.time()
for i in range(10000):
    fizzBuzz2(1000)
print("your comp:",time.time()-start_time)
start_time = time.time()
for i in range(10000):
    fizzBuzz3(1000)
print("my listcomp:",time.time()-start_time)
start_time = time.time()
for i in range(10000):
    fizzBuzz(1000)
print("classical:",time.time()-start_time)

请注意,即使预先计算“经典”方法中的模数,它也会下降到5.375272035598755秒(这很好),但是由于所有指令而仍然比listcomp更糟糕(你也会杀死listcomp速度通过调用方法来保存模数计算)。我想python并不是获得最快速度的正确语言。

答案 1 :(得分:2)

很多人已经回答了你的问题,但我想补充说还有更快的解决方案。对我来说,事后测试数字似乎是错误的,尽管你已经知道在哪些地方" Fizz"," Buzz"和" FizzBu​​zz"会发生。试试这个:

def fizz_buzz(n):
    result = []
    for i in range(1, n + 1, 15):
        result += [str(i), str(i + 1), 'Fizz',
                   str(i + 3), 'Buzz', 'Fizz',
                   str(i + 6), str(i + 7), 'Fizz', 'Buzz',
                   str(i + 10), 'Fizz', str(i + 12), str(i + 13),
                   'FizzBuzz']
    return result[:n - len(result)]

start_time = time()
for i in range(10000):
    fizz_buzz(1000)
print("fizz_buzz:", time() - start_time)

与之前的答案相比:

your comp: 3.3942723274230957
my listcomp: 2.586350202560425
classical: 3.879168748855591
fizz_buzz: 1.6374053955078125

答案 2 :(得分:1)

是的,因为在理解中,所有指令都在每个循环中进行评估。

Snippet 2中,评估最多1个if分支。因此,减少计算,并使其成为更快的替代方案。

答案 3 :(得分:1)

虽然我们正在探索实现,但这是一个比单个列表理解更快的有趣切片,但不如@ seenorth的解决方案快:

from math import ceil


def fizz_buzz(n):
    result = list(range(n))
    result[::3] = ["Fizz"] * ceil(n / 3)
    result[::5] = ["Buzz"] * ceil(n / 5)
    result[::15] = ["FizzBuzz"] * ceil(n / 15)

    return list(map(str, result))