有没有一种快速的方法可以在Python中返回相同值的Sin和Cos?

问题描述 投票:0回答:7

我需要返回一个大数组中每个元素的 sin 和 cos 值。目前我正在做:

a,b=np.sin(x),np.cos(x)

其中 x 是一些大数组。我需要保留每个结果的符号信息,所以:

a=np.sin(x)
b=(1-a**2)**0.5

不是一个选择。有没有更快的方法同时返回 sin 和 cos ?

python numpy trigonometry
7个回答
7
投票

我将建议的解决方案与 perfplot 进行了比较,发现没有什么比明确调用

sin
cos
更好的了。

重现情节的代码:

import perfplot
import numpy as np


def sin_cos(x):
    return np.sin(x), np.cos(x)


def exp_ix(x):
    eix = np.exp(1j * x)
    return eix.imag, eix.real


def cos_from_sin(x):
    sin = np.sin(x)
    abs_cos = np.sqrt(1 - sin**2)
    sgn_cos = np.sign(((x - np.pi / 2) % (2 * np.pi)) - np.pi)
    cos = abs_cos * sgn_cos
    return sin, cos


b = perfplot.bench(
    setup=lambda n: np.linspace(0.0, 2 * np.pi, n),
    kernels=[sin_cos, exp_ix, cos_from_sin],
    n_range=[2**k for k in range(20)],
    xlabel="n",
)
b.save("out.png")
b.show()

3
投票

您可以使用复数以及 e i · φ = cos(φ) + i · sin(φ)

import numpy as np
from cmath import rect
nprect = np.vectorize(rect)

x = np.arange(2 * np.pi, step=0.01)

c = nprect(1, x)
a, b = c.imag, c.real

我在这里使用 https://stackoverflow.com/a/27788291/674064 中的技巧来制作

cmath.rect()
的版本,它将接受并返回 NumPy 数组。

但这并没有在我的机器上获得任何加速:

c = nprect(1, x)
a, b = c.imag, c.real

大约需要三倍的时间(160μs)

a, b = np.sin(x), np.cos(x)

记录了我的测量结果(50.4μs)。


1
投票

通过复数的纯 numpy 版本,e = cosφ + i sinφ, 受到das-g的答案的启发。

x = np.arange(2 * np.pi, step=0.01)

eix = np.exp(1j*x)
cosx, sinx = eix.real, eix.imag

这比

nprect
更快,但仍然比
sin
cos
调用慢:

In [6]: timeit c = nprect(1, x); cosx, sinx = cos(x), sin(x)
1000 loops, best of 3: 242 us per loop

In [7]: timeit eix = np.exp(1j*x); cosx, sinx = eix.real, eix.imag
10000 loops, best of 3: 49.1 us per loop

In [8]: timeit cosx, sinx = cos(x), sin(x)
10000 loops, best of 3: 32.7 us per loop

1
投票

为了完整起见,将其合并为单个

cos()
调用的另一种方法是准备一个角度数组,其中后半部分的相移为 pi/2。

借用 Nico Schlömer 的分析代码,我们得到:

import perfplot
import numpy as np


def sin_cos(x):
    return np.sin(x), np.cos(x)


def exp_ix(x):
    eix = np.exp(1j * x)
    return eix.imag, eix.real


def cos_shift(x):
    angles = x[np.newaxis, :] + np.array(((-np.pi/2,), (0,)))
    return tuple(np.cos(angles))


perfplot.save(
    "out.png",
    setup=lambda n: np.linspace(0.0, 2 * np.pi, n),
    kernels=[sin_cos, exp_ix, cos_shift],
    n_range=[2 ** k for k in range(1, 16)],
    xlabel="n",
)

因此它比单独的

sin
/
cos
调用慢,但在某些(狭窄的)上下文中可能更方便,因为 - 从
cos()
开始 - 它只需要处理单个数组。


0
投票

使用 Numba,您可以将速度提高约 20%(如果这对您来说很重要)。

import numba
import numpy as np

x = np.random.uniform((1000,))

@numba.njit
def sincos_simple(x):
    return np.sin(x), np.cos(x)

@numba.njit
def sincos_prealloc(x):
    r = np.empty(x.shape + (2,))
    r[..., 0] = np.sin(x)
    r[..., 1] = np.cos(x)
    return r

# compile numba function (run once before timing)
sincos_simple(x)
sincos_prealloc(x)

%timeit np.sin(x), np.cos(x)
%timeit sincos_simple(x)
%timeit sincos_prealloc(x)

结果:

1.02 µs ± 16.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
927 ns ± 13.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
819 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

-1
投票

您可以利用 tan(x) 包含 sin(x) 和 cos(x) 函数的事实。因此,您可以使用 tan(x) 并使用通用变换函数检索 cos(x) 和 sin(x)。


-1
投票
def cosfromsin(x,sinx):
   cosx=absolute((1-sinx**2)**0.5)
   signx=sign(((x-pi/2)%(2*pi))-pi)
   return cosx*signx

a=sin(x)
b=cosfromsin(x,a)

我刚刚对此进行了计时,它比使用 sin 和 cos 快了约 25%。

© www.soinside.com 2019 - 2024. All rights reserved.