为什么执行 x in range(1000000000) 如此快?
2017-09-26 13:13
162 查看
在 Python 中,表达式 1000000000000000 in range(1000000000000001) 的执行速度能有多快?
判断一个元素 x 是否存在于集合 y 中最简单粗暴地方法就是迭代,每次取出一个值与之比较,如果集合中存在一个值 z 等于 x就返回 true ,它的时间复杂度是 O(n),使用哈希算法的理论时间复杂度是 O(1),二分查找的时间复杂度是 O(log n),那么 Python 究竟会采用的哪种算法来实现呢?
先来做个实验:
我们都知道 python2 中的 range 函数返回的是一个列表对象,一次性把所有的元素加载到内存,所以执行第一个表达式的时候,系统会突然感觉非常卡顿,它需要的时间是5秒多。
xrange 和 python3 中的 range 函数类似,都是返回一个迭代器对象,但是它俩的执行结果相差悬殊,让人大跌眼镜。第三个表达式所花的时间接近0秒,为何 python2 的 xrange 与 python3 中 range 函数区别这么大?为了弄明白其中的玄机,我们要理解in操作是如何执行的。根据 Python 文档 in 的规则:
如果该类实现了contains()方法,那么只要 y.contains(x) 返回 true 那么 x in y 也返回 true,反之亦然。
没有实现contains()方法,但实现了iter()方法,那么在迭代过程中如果有某个值 z==x,就返回 true,否则就是 false。
如果以上两个方法都没有实现,就看getitem()方法, 如果存在一个索引i使得 x==y[i] ,就返回 true,否则返回 false。
明白了 in 的规则之后,我们先看看 xrange 提供了哪些方法:
是的,xrange 函数只实现了 getitem 和 iter,判断 x 是 是否在 y 中需要逐个值迭代进行比较,也就是说 xrange 的时间复杂度是O(n)。
再来看看 python3 的 range 有哪些方法:
range 提供的属性比 xrange 要多很多,不仅实现了 getitem 和 iter ,还实现了 contains ,所以它会优先调用contains方法,此外,它还提供了三个属性 start、stop、step。那么究竟为什么它的执行速度会如此之快呢?来看看contains方法是如何实现的吧。
在 Python3 中,contains 并不是逐个值迭代对比,而是采用这样一种逻辑:
首先检查 x 是否 在 start 和 stop 范围之间:start <= x < stop
如果在这个区间范围,那么再根据 step 计算 x 是否刚好落在 xrange 区间中的某个值上,这里用取模的方式来判断:(x - start) % step == 0
此刻真相大白,xrange 的时间复杂度是O(1),也就是说不管 xrange(start, stop, step) 中的 stop 值多大,时间复杂度都是一个常量。所以 python3 中的 range 方法不仅可以节省内存,而且执行效率更高,所以不要再纠结学 Python2 还是 Python3 了。
也可以把它当作一到面试题来问:Python2 中的 xrange 与 python3 中的 range 有什么区别?它不仅可以考察候选者对 Python3 的熟悉程度,而且可以看出候选者对一个知识点的理解深度。
stackoverflow.com
完全理解Python迭代对象、迭代器、生成器
Membership test operations
关注公众号『一个程序员的微站』获取最新 Python 干货和有温度的内容
关注
判断一个元素 x 是否存在于集合 y 中最简单粗暴地方法就是迭代,每次取出一个值与之比较,如果集合中存在一个值 z 等于 x就返回 true ,它的时间复杂度是 O(n),使用哈希算法的理论时间复杂度是 O(1),二分查找的时间复杂度是 O(log n),那么 Python 究竟会采用的哪种算法来实现呢?
先来做个实验:
#python2 timeit.timeit('1000000000 in range(0,1000000000,10)', number=1) 5.50357640805305 timeit.timeit('1000000000 in xrange(0,1000000000,10)', number=1) 2.3025200839183526 # python3 import timeit timeit.timeit('1000000000 in range(0,1000000000,10)', number=1) 4.490355838248402e-06
我们都知道 python2 中的 range 函数返回的是一个列表对象,一次性把所有的元素加载到内存,所以执行第一个表达式的时候,系统会突然感觉非常卡顿,它需要的时间是5秒多。
xrange 和 python3 中的 range 函数类似,都是返回一个迭代器对象,但是它俩的执行结果相差悬殊,让人大跌眼镜。第三个表达式所花的时间接近0秒,为何 python2 的 xrange 与 python3 中 range 函数区别这么大?为了弄明白其中的玄机,我们要理解in操作是如何执行的。根据 Python 文档 in 的规则:
如果该类实现了contains()方法,那么只要 y.contains(x) 返回 true 那么 x in y 也返回 true,反之亦然。
没有实现contains()方法,但实现了iter()方法,那么在迭代过程中如果有某个值 z==x,就返回 true,否则就是 false。
如果以上两个方法都没有实现,就看getitem()方法, 如果存在一个索引i使得 x==y[i] ,就返回 true,否则返回 false。
明白了 in 的规则之后,我们先看看 xrange 提供了哪些方法:
dir(xrange) ['__class__','__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__new__', ...]
是的,xrange 函数只实现了 getitem 和 iter,判断 x 是 是否在 y 中需要逐个值迭代进行比较,也就是说 xrange 的时间复杂度是O(n)。
再来看看 python3 的 range 有哪些方法:
dir(range) ['__class__', '__contains__', '__getitem__', '__iter__', 'count', 'index', 'start', 'step', 'stop', ...]
range 提供的属性比 xrange 要多很多,不仅实现了 getitem 和 iter ,还实现了 contains ,所以它会优先调用contains方法,此外,它还提供了三个属性 start、stop、step。那么究竟为什么它的执行速度会如此之快呢?来看看contains方法是如何实现的吧。
在 Python3 中,contains 并不是逐个值迭代对比,而是采用这样一种逻辑:
首先检查 x 是否 在 start 和 stop 范围之间:start <= x < stop
如果在这个区间范围,那么再根据 step 计算 x 是否刚好落在 xrange 区间中的某个值上,这里用取模的方式来判断:(x - start) % step == 0
此刻真相大白,xrange 的时间复杂度是O(1),也就是说不管 xrange(start, stop, step) 中的 stop 值多大,时间复杂度都是一个常量。所以 python3 中的 range 方法不仅可以节省内存,而且执行效率更高,所以不要再纠结学 Python2 还是 Python3 了。
也可以把它当作一到面试题来问:Python2 中的 xrange 与 python3 中的 range 有什么区别?它不仅可以考察候选者对 Python3 的熟悉程度,而且可以看出候选者对一个知识点的理解深度。
stackoverflow.com
完全理解Python迭代对象、迭代器、生成器
Membership test operations
关注公众号『一个程序员的微站』获取最新 Python 干货和有温度的内容
关注
相关文章推荐
- 为什么执行JAVA程序时,会出现Exception in thread"main"java.lang.NoClassDefFoundError的错?
- 使用mybatis执行sql的时候为什么会出现Parameter index out of range (1 > number of parameters, which is 0)?
- Thinking in C++: 第1章 为什么C++会成功(改进了C的缺点,可复用C的知识与库,执行效率相当)
- 为什么执行JAVA程序时,会出现Exception in thread"main" java.lang.NoClassDefFoundError
- 【内存管理-问题实例】为什么执行了numberOfRowsInSection方法后,dataArray的retainCount就少了一个? 谁把它 release了?
- 使用mybatis执行sql的时候为什么会出现Parameter index out of range (1 > number of parameters, which is 0)?
- shouldChangeCharactersInRange方法不执行
- 为什么执行JAVA程序时,会出现Exception in thread"main" java.lang.NoClassDefFoundError的错?...
- mybatis执行sql的时候为什么会出现Parameter index out of range (1 > number of parameters, which is 0)?
- 使用mybatis执行sql的时候为什么会出现Parameter index out of range (1 > number of parameters, which is 0)?
- 为什么执行JAVA程序时,会出现Exception in thread"main" java.lang.NoClassDefFoundError的错
- 为什么执行JAVA程序时,会出现Exception in thread"main" java.lang.NoClassDefFoundError的错?...
- 为什么在main函数中run as application,执行正常;而通过tomcat发布后,就报错classnotfound?
- Value does not fall within the expected range in excel word addin HResult 2147024809
- 为什么二进制文件与文本文件存入同样的数据,文件大小差异会这么大?(from <<Thinking in C++>>'s execise)
- Sum All Numbers in a Range
- 为什么每天都如此开心
- Java为什么解释执行时不直接解释源码?
- 为什么linux下多线程程序如此消耗虚拟内存
- 自定的 HttpModule 为什么总是执行两次