Python LeetCode问题的运行时差异原因(打开锁)
LeetCod问题链接: (第页) 下面是代码#1从源代码和目标代码执行bfs:Python LeetCode问题的运行时差异原因(打开锁),python,algorithm,time-complexity,Python,Algorithm,Time Complexity,LeetCod问题链接: (第页) 下面是代码#1从源代码和目标代码执行bfs: from collections import deque class Solution: def openLock(self, deadends: List[str], target: str) -> int: deadend = set(deadends) if target in deadend or '0000' in deadend:
from collections import deque
class Solution:
def openLock(self, deadends: List[str], target: str) -> int:
deadend = set(deadends)
if target in deadend or '0000' in deadend:
return -1
if target == '0000':
return 0
def getNextCombi(combi: str) -> List[str]:
res = []
for i in range(4):
nextCombi1 = combi[:i] + str((int(combi[i:i + 1]) + 1) % 10) + combi[i + 1:]
nextCombi2 = combi[:i] + str((int(combi[i:i + 1]) - 1) % 10) + combi[i + 1:]
if nextCombi1 not in deadend:
res.append(nextCombi1)
if nextCombi2 not in deadend:
res.append(nextCombi2)
return res
sourceQueue = deque(['0000'])
targetQueue = deque([target])
sourceSeen = {'0000': 0}
targetSeen = {target: 0}
while len(sourceQueue) != 0 and len(targetQueue) != 0:
sourceCombi = sourceQueue.popleft()
targetCombi = targetQueue.popleft()
for nextCombi in getNextCombi(sourceCombi):
if nextCombi not in sourceSeen:
sourceSeen[nextCombi] = sourceSeen[sourceCombi] + 1
sourceQueue.append(nextCombi)
if nextCombi in targetSeen:
return sourceSeen[nextCombi] + targetSeen[nextCombi]
for nextCombi in getNextCombi(targetCombi):
if nextCombi not in targetSeen:
targetSeen[nextCombi] = targetSeen[targetCombi] + 1
targetQueue.append(nextCombi)
if nextCombi in sourceSeen:
return sourceSeen[nextCombi] + targetSeen[nextCombi]
return -1
下面是代码#2从源代码执行bfs:
from collections import deque
class Solution:
def openLock(self, deadends: List[str], target: str) -> int:
deadend = set(deadends)
if target in deadend or '0000' in deadend:
return -1
if target == '0000':
return 0
def getNextCombi(combi: str) -> List[str]:
res = []
for i in range(4):
nextCombi1 = combi[:i] + str((int(combi[i:i + 1]) + 1) % 10) + combi[i + 1:]
nextCombi2 = combi[:i] + str((int(combi[i:i + 1]) - 1) % 10) + combi[i + 1:]
if nextCombi1 not in deadend:
res.append(nextCombi1)
if nextCombi2 not in deadend:
res.append(nextCombi2)
return res
sourceQueue = deque(['0000'])
sourceSeen = {'0000': 0}
while len(sourceQueue) != 0:
sourceCombi = sourceQueue.popleft()
for nextCombi in getNextCombi(sourceCombi):
if nextCombi not in sourceSeen:
sourceSeen[nextCombi] = sourceSeen[sourceCombi] + 1
sourceQueue.append(nextCombi)
if nextCombi == target:
return sourceSeen[nextCombi]
代码#1在LeetCode上给出了大约120毫秒的速度,
代码#2在LeetCode上提供了大约640ms的速度。
我试了好几次,所以我相信算法本身有很大的不同
为什么1比2快得多?
我看不出在时间复杂性方面有什么不同。
是否仅仅因为LeetCode中使用的示例在#1上更快
我对这两种代码的分析:
我认为它们具有相同的时间复杂度O(1),因为在最坏的情况下,它会遍历所有可能的组合,即4^9。我不确定我的时间复杂度,但我认为我做BFS源->目标,做BFS源->满足是正确的。我实现了C++程序来测量两种算法中的运行的操作码和行数。为什么C++,而不是Python?不是因为C++速度,而是因为只有Python C API提供足够的功能来精细化代码跟踪函数,以设置它来测量操作码和行。
下一个C++代码的测量结果(输出)为:
Traced 'algo1': time 3441700 nanoseconds, 1455 lines, 9304 opcodes,
2365 avg ns/line, 369 avg ns/opcode.
Traced 'algo2': time 18011100 nanoseconds, 22678 lines, 155640 opcodes,
794 avg ns/line, 115 avg ns/opcode.
使用下一个测试输入(取自LeetCode示例)测量两个算法:
因此,algo1比algo2快得多,algo1只执行9304个操作码,而algo2执行155640个操作码,即16倍多的操作码。它是什么意思?操作码
——它是Python内部最小的操作,与汇编程序内部的指令相同,但这里的操作码是Python虚拟机内部的指令。尽管某些操作码可能比其他操作码花费更长的时间,但它仍然是一个很好的定量度量(操作码的数量)
这意味着即使您的两个算法都是
O(1)
,每个算法中执行的操作数仍然是非常不同的。还要记住,任何复杂度O(f(N))
实际上意味着运行时间是,我建议首先分析这两种算法的时间复杂度。如果您需要帮助,请首先尝试并回答您的问题来描述您的分析。请重复并从开始。首先,您需要关注运行时间(挂钟或CPU时间)或算法复杂性。这不是一回事。其次,您需要分析、分析或以其他方式理解代码,以了解什么会影响您选择的度量。最后,您需要与我们分享,并将您的问题集中在您不了解的细节上。“为什么一个比另一个快”是不成熟的:它试图把你的分析责任转移给任何愿意回复你帖子的人。我不知道为什么我会被评判为我的问题。。。这只是一个简单的问题,为什么运行时是不同的(无论是算法上的,还是仅仅因为Leetcode是如何的,等等)。我认为最坏情况下的时间复杂度只是O(1),因为它是O(4^10),我不确定我的分析,所以为什么不像我那样问呢?@jginso7一般来说,如果两个算法是O(1)
并不意味着它们有相同的实际运行时间,因为任何复杂度O(f(N))
实际上意味着时间
deadends = ['0201', '0101', '0102', '1212', '2002'], target = '0202'
397 function calls in 0.003 seconds
Ordered by: call count
ncalls tottime percall cumtime percall filename:lineno(function)
161 0.001 0.000 0.001 0.000 {method 'append' of 'list' objects}
101 0.000 0.000 0.000 0.000 {method 'append' of 'collections.deque' objects}
22 0.000 0.000 0.000 0.000 {method 'popleft' of 'collections.deque' objects}
22 0.000 0.000 0.000 0.000 {built-in method builtins.hasattr}
22 0.000 0.000 0.000 0.000 {built-in method builtins.len}
21 0.001 0.000 0.002 0.000 C:\dev\tmp_code\main.py:17(getNextCombi)
1 0.001 0.001 0.003 0.003 C:\dev\tmp_code\main.py:7(openLock)
1 0.000 0.000 0.000 0.000 C:\bin\Python39\lib\typing.py:256(inner)
1 0.000 0.000 0.000 0.000 C:\bin\Python39\lib\cProfile.py:117(__exit__)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
6261 function calls in 0.055 seconds
Ordered by: call count
ncalls tottime percall cumtime percall filename:lineno(function)
3093 0.011 0.000 0.011 0.000 {method 'append' of 'list' objects}
821 0.003 0.000 0.003 0.000 {method 'append' of 'collections.deque' objects}
391 0.001 0.000 0.001 0.000 {built-in method builtins.hasattr}
390 0.021 0.000 0.040 0.000 C:\dev\tmp_code\main.py:17(getNextCombi)
390 0.001 0.000 0.001 0.000 {method 'popleft' of 'collections.deque' objects}
390 0.001 0.000 0.001 0.000 {built-in method builtins.len}
1 0.009 0.009 0.055 0.055 C:\dev\tmp_code\main.py:7(openLock)
1 0.000 0.000 0.000 0.000 C:\bin\Python39\lib\typing.py:256(inner)
1 0.000 0.000 0.000 0.000 C:\bin\Python39\lib\cProfile.py:117(__exit__)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}