我不知道为什么会发生这种情况。我在搞乱一些列表,我需要一个从{0到for
的{{1}}循环,其中n是列表的长度。但是代码速度非常慢,所以经过一番研究后我发现问题出现在范围生成中。演示示例代码:
log(n, 2)
输出
n = len([1,2,3,4,5,6,7,8])
k = 8
timeit('range(log(n, 2))', number=2, repeat=3) # Test 1
timeit('range(log(k, 2))', number=2, repeat=3) # Test 2
测试次数很少(我不希望它运行超过10分钟),但它已经显示2 loops, best of 3: 2.2 s per loop
2 loops, best of 3: 3.46 µs per loop
比仅使用整数的对数的对应物慢几个数量级。这真的很令人惊讶,我不知道为什么会发生这种情况。可能是我的电脑上的问题,可能是Sage问题或Python错误(我在Python上没有尝试过相同的问题)。
使用range(log(n, 2))
代替xrange
也无济于事。此外,如果您使用range
获得数字,则测试1以2的相同速度运行。
有人知道会发生什么吗? 谢谢!
答案 0 :(得分:13)
好悲伤 - 我认识到这一点。它与我的一个trac #12121有关。首先,由于无聊的原因,使用Python int
而不是Sage Integer
会产生额外的开销:
sage: log(8, 2)
3
sage: type(log(8, 2))
sage.rings.integer.Integer
sage: log(8r, 2)
log(8)/log(2)
sage: type(log(8r, 2))
sage.symbolic.expression.Expression
sage: %timeit log(8, 2)
1000000 loops, best of 3: 1.4 us per loop
sage: %timeit log(8r, 2)
1000 loops, best of 3: 404 us per loop
(r
后缀表示“原始”,并阻止Sage预处理器将文字2
包装到Integer(2)
)
然后它变得奇怪。为了产生range
消耗的int,Sage必须弄清楚如何将log(8)/log(2)
变为3,并且事实证明她做了最糟糕的事情。抄袭我原来的诊断(比照适用):
首先,她检查这个对象是否有自己的方式来获取int,但事实并非如此。所以她用log(8)/ log(2)构建了一个RealInterval对象,事实证明这是她能做的最糟糕的事情!她检查区间的下部和上部是否同意[在地板上,我的意思](这样她就知道地板是什么)。但在这种情况下,因为它确实是一个整数!这总是看起来像:
sage: y = log(8)/log(2)
sage: rif = RealIntervalField(53)(y)
sage: rif
3.000000000000000?
sage: rif.endpoints()
(2.99999999999999, 3.00000000000001)
这两个界限的楼层并不相等,所以Sage决定她还没有解决问题,她不断将精度提高到20000位,看她是否可以证明它们是......但是通过建设,它永远不会起作用。最后,她放弃并试图简化它,这成功了:
sage: y.simplify_full()
3
证明没有言语,这是完全可分割的案件的反常属性:
sage: %timeit range(log(8r, 2))
1 loops, best of 3: 2.18 s per loop
sage: %timeit range(log(9r, 2))
1000 loops, best of 3: 766 us per loop
sage: %timeit range(log(15r, 2))
1000 loops, best of 3: 764 us per loop
sage: %timeit range(log(16r, 2))
1 loops, best of 3: 2.19 s per loop
答案 1 :(得分:1)
Python 2允许范围(some_float),但不推荐使用它并且在python 3中不起作用。
代码示例未指定输出。但我们可以走过它。首先,timeit需要一个完整的脚本,脚本调用timeit中的导入不会被使用:
>>> timeit('range(log(8,2))')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python2.6/timeit.py", line 226, in timeit
return Timer(stmt, setup, timer).timeit(number)
File "/usr/lib64/python2.6/timeit.py", line 192, in timeit
timing = self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
NameError: global name 'log' is not defined
如果您将导入添加到正在计时的脚本中,则会包含设置时间:
>>> timeit('from math import log;range(log(8,2))')
3.7010221481323242
如果你将导入移动到设置,它会更好,但是一次性的定时是众所周知的不准确:
>>> timeit('range(log(8,2))',setup='from math import log')
1.9139349460601807
最后,运行它多次,你得到一个很好的数字:
>>> timeit('range(log(8,2))',setup='from math import log',number=100)
0.00038290023803710938
答案 2 :(得分:1)
这看起来像是一个Sage bug。
我创建了一个新的笔记本并做了这个:
n = len([1,2,3,4,5,6,7,8])
k = 8
timeit('range(log(n, 2))', number=2, repeat=3) # Test 1
timeit('range(log(len([1,2,3,4,5,6,7,8]), 2))', number=2, repeat=3) # Test 1.5
timeit('range(log(k, 2))', number=2, repeat=3) # Test 2
测试1.5与测试1一样慢。但如果你以任何方式将其分解 - 取消range
,甚至添加m=n+0
并使用m
代替{ {1}},它下降到微秒。
很明显,Sage在评估表达时会尝试做一些复杂的事情,并且感到困惑。
要验证这一点,请参阅普通的ipython:
n
他们都像你期望的一样快。
那么......你怎么办呢?
好吧,您可能想要尝试追踪Sage错误并将其归档上游。但与此同时,您可能需要在代码中使用解决方法。
如上所述,仅仅执行n = len([1,2,3,4,5,6,7,8])
k = 8
%timeit 'range(log(n, 2))'
%timeit 'range(log(len([1,2,3,4,5,6,7,8]), 2))'
%timeit 'range(log(k, 2))'
并使用m = n+0
代替m
似乎可以加快速度。看看它是否适合你?
答案 3 :(得分:-1)
首先使用log(x, 2)
(又名ld()
)并不是一个好主意。我建议使用移位int值来实现ld()
:
n = len(array)
while n:
n >>= 1
# perform the loop stuff
通过这种方式,您可以使用range()
和log()
避免所有这些违规行为。
在正常情况下,调用log()
应该比在int上进行简单的位移动需要更多的时间。例子:
>>> timeit('for i in range(int(math.log(8, 2))): pass', setup='import math')
0.6762251853942871
>>> timeit('n = 8\nwhile n:\n n >>= 1')
0.24107813835144043
n
的值越大,差异越小。对于n = 10000
,我得到了0.8163230419158936和0.8106038570404053,但这应该是因为与循环初始化相比,循环体将占用大部分时间。