我很好奇为什么它的繁殖速度比在python中强大得多(尽管从我读过的内容来看,这在很多其他语言中也是如此)。例如,它做得快得多
x*x
大于
x**2
我认为**运算符更通用,也可以处理分数幂。但是,如果这就是为什么它如此慢,为什么它不执行int指数检查然后只是进行乘法?
编辑:以下是我尝试的一些示例代码...
def pow1(r, n):
for i in range(r):
p = i**n
def pow2(r, n):
for i in range(r):
p = 1
for j in range(n):
p *= i
现在,pow2只是一个简单的例子,显然没有优化!
但即便如此,我发现使用n = 2且r = 1,000,000,则pow1需要~2500ms而pow2需要~1700ms。
我承认,对于大的n值,pow1确实比pow2快得多。但这并不太令人惊讶。
答案 0 :(得分:23)
基本上天真的乘法是O(n),具有非常低的常数因子。取幂是O(log n),具有更高的常数因子(有特殊情况需要测试......分数指数,负指数等)。编辑:要清楚,那就是O(n),其中n是指数。
当然,对于小n来说,天真的方法会更快,你只是真正实现了一小部分指数数学,所以你的常数因子可以忽略不计。
答案 1 :(得分:6)
添加支票也是一项费用。你总是想在那里检查吗?编译语言可以检查一个常量指数,看看它是否是一个相对较小的整数,因为没有运行时成本,只是一个编译时成本。解释性语言可能无法进行检查。
除非语言指定了这种细节,否则由特定的实现决定。
Python不知道你要为它提供什么样的指数分布。如果它将是99%的非整数值,您是否希望代码每次检查一个整数,使运行时更慢?
答案 2 :(得分:3)
在指数检查中执行此操作会减慢非常轻微的两次幂的情况,因此不一定是胜利。然而,在指数事先已知的情况下(例如,使用文字2),生成的字节码可以通过简单的窥视孔优化来优化。据推测,这根本不值得做(这是一个相当具体的案例)。
这是一个快速的概念证明,可以进行这样的优化(可用作装饰器)。注意:您需要byteplay模块来运行它。
import byteplay, timeit
def optimise(func):
c = byteplay.Code.from_code(func.func_code)
prev=None
for i, (op, arg) in enumerate(c.code):
if op == byteplay.BINARY_POWER:
if c.code[i-1] == (byteplay.LOAD_CONST, 2):
c.code[i-1] = (byteplay.DUP_TOP, None)
c.code[i] = (byteplay.BINARY_MULTIPLY, None)
func.func_code = c.to_code()
return func
def square(x):
return x**2
print "Unoptimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000)
square = optimise(square)
print "Optimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000)
给出了时间:
Unoptimised : 6.42024898529
Optimised : 4.52667593956
<强> [编辑] 强>
实际上,考虑一下,有一个很好的理由说明为什么这种优化没有完成。无法保证有人不会创建一个用户定义的类来覆盖__mul__
和__pow__
方法,并为每个方法执行不同的操作。安全地执行此操作的唯一方法是,如果您可以保证堆栈顶部的对象是具有相同结果的对象“x **2
”和“x*x
”,那么解决这个问题的方法是更难。例如。在我的例子中,这是不可能的,因为任何对象都可以传递给square函数。
答案 3 :(得分:3)
带有二进制求幂的b ^ p的实现
def power(b, p):
"""
Calculates b^p
Complexity O(log p)
b -> double
p -> integer
res -> double
"""
res = 1
while p:
if p & 0x1: res *= b
b *= b
p >>= 1
return res
答案 4 :(得分:1)
我怀疑没有人期待这一切都那么重要。通常情况下,如果你想进行严肃的计算,你可以用Fortran或C或C ++或类似的东西来做(也许可以用Python调用它们)。
将所有内容视为exp(n * log(x))在n不是整数或非常大的情况下效果很好,但对于小整数来说效率相对较低。检查n是否是足够小的整数确实需要时间,并且会增加复杂性。
检查是否值得,取决于预期的指数,在这里获得最佳性能的重要性,以及额外复杂性的成本。显然,Guido和其他Python团伙决定检查不值得。
如果您愿意,可以编写自己的重复乘法函数。
答案 5 :(得分:0)
x x x x x怎么样? 它仍然比x ** 5快吗?
当int exponents变大时,获取幂可能比乘法更快。 但实际交叉发生的数量取决于各种条件,因此在我看来,这就是为什么在语言/库级别没有完成(或无法完成)优化的原因。但是用户仍然可以针对某些特殊情况进行优化:)