【问题标题】:Is there a fast Way to return Sin and Cos of the same value in Python?有没有一种快速的方法可以在 Python 中返回相同值的 Sin 和 Cos?
【发布时间】:2015-09-04 11:48:53
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • sin 90-x = cos x
  • 我是否正确理解了您的问题?基本上你在问:如果我已经计算了np.sin(x),我可以使用这些信息来获得cos(x),而不是评估np.cos(x)吗?
  • OP 间接地指的是一些数学库(和数学硬件)具有sincos 函数,该函数同时返回给定参数的正弦和余弦。因此,想知道 numpy 是否可以做到这一点并不是没有道理的,IMO。
  • 您可以使用 tan(x) 并使用通用转换函数检索 cos(x) 和 sin(x)。不过不知道是不是更快,你应该试试....
  • 看来numpy目前没有sincos函数。见Implement sincos()

标签: python numpy trigonometry


【解决方案1】:

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

重现情节的代码:

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


perfplot.save(
    "out.png",
    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",
)

【讨论】:

    【解决方案2】:

    您可以使用复数和 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)。

    【讨论】:

    • 是的,我尝试过类似的方法,但不幸的是它慢了很多
    【解决方案3】:

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

    【讨论】:

    • 如何获得正确的标志信息?你只检查 x 在哪个象限吗?
    • 很好,我忽略了这也会丢失符号信息......如果我必须检索角度的象限,我可以在我的原始帖子中使用替代方法。问题依然存在;如何快速确定数组中所有元素的象限?
    • 我想检索标志确实失去了这种技术的优势
    【解决方案4】:
    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%。

    【讨论】:

    • 你实际时间是多少?当您计时时数组x 有多大?当我预先计算sinx 并比较cos(x)cosfromsin(x, sinx) 的时间时,cosfromsinx 更慢。
    • 我也是这样做的。我传递给 cos 和 cosfrom sin 的对象是一个二维 numpy 数组,尺寸约为 2000 * 1000
    • 这个cosfromsin(x),给定sin(x),对于这样的数组大小也比cos(x)慢~4倍(对于小数组更糟)。注意:absolute 可以删除。 signcos = (np.int_((x - pi_2) // pi) & 1) * 2 - 1 速度有所加快,但仍不会击败。
    • 这实际上相当慢。
    【解决方案5】:

    通过复数的纯 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 快,但仍然比sincos 调用慢:

    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
    

    【讨论】:

    • 好主意,但仍然比显式调用 sin 和 cos 慢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 2019-10-18
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多