[英]What is the most efficient way of doing square root of sum of square of two numbers?
我正在尋找一種更有效,最短的方法來執行兩個或多個數字的平方和的平方根。 我實際上正在使用numpy
和此代碼:
np.sqrt(i**2+j**2)
這似乎比以下方法快五倍:
np.sqrt(sum(np.square([i,j])))
(i和j代表數字!)
我想知道是否已經有一個內置函數可以用更少的代碼來更有效地執行這一非常常見的任務。
對於i != j
的情況,不可能使用np.linalg.norm
進行此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
將適用於單個浮點和數組,而math.sqrt
僅適用於標量。
中型陣列的一些時間安排:
i = np.random.rand(100000)
j = np.random.rand(100000)
%timeit np.sqrt(i**2 + j**2)
# 1.45 ms ± 314 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit (i*i + j*j)**0.5
# 1.21 ms ± 78.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
您可以嘗試重寫程序,使i
和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)
如您所見,第一個變體(使用數字數組作為輸入)比使用python for循環的第二個變體快300倍。 這樣做的原因是,在第一個示例中,所有計算都由numpy執行(在內部用c實現,因此速度非常快),而在第二個示例中,numpy代碼和常規python代碼(for循環)交織,使得執行速度要慢得多。
如果您確實想提高程序的性能,建議您重寫它,以便您可以在兩個numpy數組上執行一次函數,而不必為每對數字調用它。
在這種情況下, 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
我根據答案進行了一些比較,似乎更快的方法是先使用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)')
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.