我正在寻找一种更有效,最短的方法来执行两个或多个数字的平方和的平方根。我实际上正在使用numpy
和以下代码:
np.sqrt(i**2+j**2)
这似乎比以下速度快五倍:
np.sqrt(sum(np.square([i,j])))
(i和j代表数字!)
我想知道是否已经有一个内置函数可以用更少的代码来更有效地执行这一非常常见的任务。
答案 0 :(得分:2)
对于i != j
,无法使用np.linalg.norm
进行此操作,因此我建议以下内容:
(i*i + j*j)**0.5
如果i
和j
是单个浮点,则这比np.sqrt(i**2+j**2)
快5倍。如果i
和j
是numpy数组,则速度要快20%(由于将正方形替换为i*i
和j*j
。如果不替换正方形,则效果等于np.sqrt(i**2+j**2)
。
使用单个浮点数的一些计时:
i = 23.7
j = 7.5e7
%timeit np.sqrt(i**2 + j**2)
# 1.63 µs ± 15.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit (i*i + j*j)**0.5
# 336 ns ± 7.38 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit math.sqrt(i*i + j*j)
# 321 ns ± 8.21 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
math.sqrt
比(i*i + j*j)**0.5
稍快,但这以牺牲灵活性为代价:(i*i + j*j)**0.5
将在单个浮点AND数组上运行,而{ {1}}仅适用于标量。
中型阵列的一些时间安排:
math.sqrt
答案 1 :(得分:2)
您可以尝试重写程序,使j
和import numpy as np
i = np.arange(10000)
j = np.arange(10000)
%%timeit
np.sqrt(i**2+j**2)
# 74.1 µs ± 2.74 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit
for idx in range(len(i)):
np.sqrt(i[idx]**2+j[idx]**2)
# 25.2 ms ± 1.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
是数组而不是单个数字,而不是优化此相当简单的函数调用(假设您需要多次调用该函数)不同的输入)。看到这个小的基准:
GET
如您所见,第一个变体(使用数字数组作为输入)比使用python for循环的第二个变体快300倍。这样做的原因是,在第一个示例中,所有计算都由numpy执行(在内部用c实现,因此速度非常快),而在第二个示例中,numpy代码和常规python代码(for循环)交织,使得执行速度要慢得多。
如果您确实想提高程序的性能,建议您重写它,以便您可以在两个numpy数组上执行一次函数,而不必为每对数字调用它。
答案 2 :(得分:1)
在这种情况下,numexpr
模块可能会更快。该模块避免了中间缓冲,因此对于某些操作来说速度更快:
i = np.random.rand(100000)
j = np.random.rand(100000)
%timeit np.sqrt(i**2 + j**2)
# 1.34 ms
import numexpr as ne
%timeit ne.evaluate('sqrt(i**2+j**2)')
#370 us
答案 3 :(得分:0)
我根据答案进行了一些比较,似乎更快的方法是先使用math
模块,然后再使用math.hypot(i + j)
,但最好的折衷方法是不导入任何模块而使用(i*i + j*j)**0.5
即使没有那么明确。
from timeit import timeit
import matplotlib.pyplot as plt
tests = [
"np.sqrt(i**2+j**2)",
"np.sqrt(sum(np.square([i,j])))",
"(i*i + j*j)**0.5",
"math.sqrt(i*i + j*j)",
"math.hypot(i,j)",
"np.linalg.norm([i,j])",
"ne.evaluate('sqrt(i**2+j**2)')",
"np.hypot(i,j)"]
results = []
lengths = []
for test in tests:
results.append(timeit(test,setup='i = 7; j = 4;\
import numpy as np; \
import math; \
import numexpr as ne', number=1000000))
lengths.append(len(test))
indx = range(len(results))
plt.bar(indx,results)
plt.xticks(indx,tests,rotation=90)
plt.yscale('log')
plt.ylabel('Time (us)')