Python集合.计数器效率
我使用以下代码来实现一个函数,该函数在字符串s中查找字符串p的所有字符串.
class Solution(object): def findAnagrams(self,s,p): """ :type s: str :type p: str :rtype: List[int] """ ans = list() pcnt = collections.Counter(p) for i in range(len(s)): if collections.Counter(s[i:i+len(p)]) == pcnt: ans.append(i) return ans 当在大长度输入字符串s上运行时,它在在线代码测试系统中给出了“超出时间限制”的错误.但是,以下代码将不会出现此类问题: class Solution(object): def findAnagrams(self,p): """ :type s: str :type p: str :rtype: List[int] """ ls,lp = len(s),len(p) cp = collections.Counter(p) cs = collections.Counter() ans = [] for i in range(ls): cs[s[i]] += 1 if i >= lp: cs[s[i - lp]] -= 1 if cs[s[i - lp]] == 0: del cs[s[i - lp]] if cs == cp: ans.append(i - lp + 1) return ans 我能知道为什么吗?似乎两种解决方案都使用两个最大尺寸为len(p)的计数器? 解决方法
要了解为什么某些代码比其他代码运行得更快,您应该对其进行分析.在Python中,开始分析的最简单方法是运行:
python -m cProfile <script.py> 在我的例子中,我写了一个简单的脚本,调用慢速解决方案或快速解决方案: # Pasted code from original question. # Also renamed the slow version to `SlowSolution` and the fast version to `FastSolution`. ... # solution = FastSolution() solution = SlowSolution() print(solution.findAnagrams('abcdefg' + 'a' * 10000,'gfedcba' + 'a' * 10000)) 然后我使用SlowSolution和FastSolution运行脚本.以下是使用SlowSolution输出的探查器结果: $python -m cProfile counter.py [0] 100204 function calls (100192 primitive calls) in 2.557 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 10008 0.015 0.000 2.538 0.000 __init__.py:516(__init__) 10008 0.009 0.000 2.522 0.000 __init__.py:585(update) 7 0.000 0.000 0.000 0.000 _collections_abc.py:392(__subclasshook__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:16(__init__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:20(__enter__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:26(__exit__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:36(__init__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:52(_commit_removals) 9 0.000 0.000 0.000 0.000 _weakrefset.py:58(__iter__) 20022 0.007 0.000 0.007 0.000 _weakrefset.py:70(__contains__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:81(add) 10008 0.010 0.000 0.017 0.000 abc.py:178(__instancecheck__) 7/1 0.000 0.000 0.000 0.000 abc.py:194(__subclasscheck__) 1 0.000 0.000 2.557 2.557 counter.py:1(<module>) 1 0.000 0.000 0.000 0.000 counter.py:17(FastSolution) 1 0.000 0.000 0.000 0.000 counter.py:3(SlowSolution) 1 0.017 0.017 2.556 2.556 counter.py:4(findAnagrams) 10008 2.490 0.000 2.490 0.000 {built-in method _collections._count_elements} 2 0.000 0.000 0.000 0.000 {built-in method builtins.__build_class__} 1 0.000 0.000 2.557 2.557 {built-in method builtins.exec} 7 0.000 0.000 0.000 0.000 {built-in method builtins.getattr} 10008 0.005 0.000 0.022 0.000 {built-in method builtins.isinstance} 8/2 0.000 0.000 0.000 0.000 {built-in method builtins.issubclass} 30024 0.003 0.000 0.003 0.000 {built-in method builtins.len} 1 0.000 0.000 0.000 0.000 {built-in method builtins.print} 7 0.000 0.000 0.000 0.000 {method '__subclasses__' of 'type' objects} 14 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects} 1 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 7 0.000 0.000 0.000 0.000 {method 'remove' of 'set' objects} 和FastSolution: $python -m cProfile counter.py [0] 146 function calls (134 primitive calls) in 0.005 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 2 0.000 0.000 0.001 0.000 __init__.py:516(__init__) 7 0.000 0.000 0.000 0.000 __init__.py:536(__missing__) 2 0.000 0.000 0.001 0.000 __init__.py:585(update) 7 0.000 0.000 0.000 0.000 _collections_abc.py:392(__subclasshook__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:16(__init__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:20(__enter__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:26(__exit__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:36(__init__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:52(_commit_removals) 9 0.000 0.000 0.000 0.000 _weakrefset.py:58(__iter__) 8 0.000 0.000 0.000 0.000 _weakrefset.py:70(__contains__) 7 0.000 0.000 0.000 0.000 _weakrefset.py:81(add) 1 0.000 0.000 0.000 0.000 abc.py:178(__instancecheck__) 7/1 0.000 0.000 0.000 0.000 abc.py:194(__subclasscheck__) 1 0.000 0.000 0.005 0.005 counter.py:1(<module>) 1 0.000 0.000 0.000 0.000 counter.py:17(FastSolution) 1 0.004 0.004 0.005 0.005 counter.py:18(findAnagrams) 1 0.000 0.000 0.000 0.000 counter.py:3(SlowSolution) 1 0.001 0.001 0.001 0.001 {built-in method _collections._count_elements} 2 0.000 0.000 0.000 0.000 {built-in method builtins.__build_class__} 1 0.000 0.000 0.005 0.005 {built-in method builtins.exec} 7 0.000 0.000 0.000 0.000 {built-in method builtins.getattr} 1 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance} 8/2 0.000 0.000 0.000 0.000 {built-in method builtins.issubclass} 6 0.000 0.000 0.000 0.000 {built-in method builtins.len} 1 0.000 0.000 0.000 0.000 {built-in method builtins.print} 7 0.000 0.000 0.000 0.000 {method '__subclasses__' of 'type' objects} 14 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects} 1 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 7 0.000 0.000 0.000 0.000 {method 'remove' of 'set' objects} 一开始读取的输出有点奇怪,但我们真的对tottime列很感兴趣.这告诉我们在特定功能中花费了多少时间. 如您所见,该脚本几乎将所有时间都花在{内置方法_collections._count_elements}中.这是一个计数器使用的内部方法,我们可以推断每次创建计数器时都会调用它(如collections.Counter(p)). 为了使代码更快,您应该更少次地调用collections.Counter(…)和/或使用更短的字符串.在慢速版本中,您计算??len(p)个字符len(s)次.这有一个O(sp)的运行时间是二次的并解释为什么它对大输入这么慢. 另一方面,更快的解决方案将s的每个字符精确计数一次,从而为其提供O(s p)的运行时间.这速度更快,并且可以通过更大的输入进行扩展. 有关Python中的性能分析的更多信息,请参阅How can you profile a python script? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |