Python中列表推导式初始化二维矩阵对角线为何比外置for循环慢
两种初始化写法效率差异的核心原因
- 操作量级的本质差异
嵌套列表推导式的写法需要执行n*n次Python层面的判断和赋值操作,以1000*1000的矩阵为例,总操作量是100万次。而快的写法中,[[False]*n for _ in range(n)]的批量赋值逻辑是CPython底层C语言实现的,几乎没有Python层面的循环开销,后续仅需要执行n次对角线赋值操作,总有效操作量仅为千次级,二者操作量差了3个数量级。 - C底层实现与Python字节码的效率差
[False]*1000这类序列乘法操作是在CPython解释器的C代码层完成的,不需要逐次执行Python字节码,执行效率比Python层面的循环、条件判断高数十倍。嵌套列表推导的每一次条件判断、赋值都需要走Python字节码调度,累计开销极高。 - 冗余判断的额外开销
慢写法中每一个元素都需要执行一次i==j的相等判断,这类单次开销极低的操作放大到百万次量级后,累计占用的时间也会非常可观。快写法完全规避了这类冗余判断,仅对对角线位置做精准赋值,没有额外开销。
在LeetCode的评测场景下,时间限制阈值通常比较严格,当输入字符串长度较大时,慢写法的初始化开销足以让总耗时超过限制,切换为外置循环初始化后省下的时间足以满足评测要求。
附测试代码验证:
# 慢版本 from time import perf_counter s=perf_counter() dpArray=[[ True if i==j else False for j in range(1000)] for i in range(1000) ] print(f"Initialized in {perf_counter() - s:0.4f} seconds") # 快版本 s=perf_counter() dpArray=[[False]*1000 for i in range(1000) ] for i in range(1000): dpArray[i][i]=True print(f"Initialized diagonal in {perf_counter() - s:0.4f} seconds")
运行结果参考:
Initialized in 0.0645 seconds Initialized diagonal in 0.0095 seconds
对应最长回文子串题解的两种实现:
# 慢版本题解 class Solution: def longestPalindrome(self, s: str) -> str: dp = [[ True if i==j else False for j in range(len(s))] for i in range(len(s)) ] ans=s[0] for j in range(len(s)): for i in (range(j)): if s[i]==s[j] and (dp[i+1][j-1] or j==i+1): dp[i][j]=True if j-i+1>len(ans): ans=s[i:j+1] return ans
# 快版本题解 class Solution: def longestPalindrome(self, s: str) -> str: dp = [[False]*len(s) for _ in range(len(s)) ] for i in range(len(s)): dp[i][i]=True ans=s[0] for j in range(len(s)): for i in range(j): if s[i]==s[j] and (dp[i+1][j-1] or j==i+1): dp[i][j]=True if j-i+1>len(ans): ans=s[i:j+1] return ans
内容的提问来源于stack exchange,提问作者Suneel
相关产品推荐
相关产品推荐

