【问题标题】:Numpy: Create sine wave with exponential decayNumpy:创建具有指数衰减的正弦波
【发布时间】:2019-07-20 12:01:45
【问题描述】:

我是一个 Numpy 新手。

我想创建一个包含一百万个数字的数组,它的正弦波的幅度呈指数衰减。

换句话说,我希望每个单元格n 的值为sin(n) * 2 ** (-n * factor)

最有效的方法是什么?

【问题讨论】:

  • 充分尊重双方(提供的任何一个答案的提问者和作者)恕我直言,缺少一个基本信息——即确切的信息是什么请求的 "效率" 的度量:仅是速度(以防 O/P 在 [TIME] 域内发生战斗)?它是内存占用吗,以防战斗进入 [SPACE] 域并且问题扩展的影响开始伤害我们?或者,如果我们在 [TIME] 和 [SPACE] 领域都打架,一旦使用超出教科书大小 1E6、1E9、1E12、1E15+ 的数据规模,这是否是某种形式的双重麻烦
  • 嗯,我想我会关心时间和空间。
  • @cool-RR 当然,这是最常见的战斗。猜猜你会在问题中表达出来。 对于 1E9、1E12 和 1E15 数据点,最快的方法是什么,但占用的内存最少? Apollo 导航计算机非常可爱,可以在 2k*16b 的写入范围内完成 -能够存储,只有 24 个 1k*16b 大小的预接线铁氧体环 ROM 块和代码/表。向麻省理工学院致敬,向玛格丽特·汉密尔顿女士致敬,正是这位女性,正好在 50 年前通过智能设计的操作系统拯救了登月过载的 AGC 计算机等待...
  • 如果您愿意在numpy 之外使用另一个模块,那么@max9111 的回答是最好的,您应该接受它。这个答案在时间上是最好的,而且在内存使用方面也可能是最好的。

标签: python numpy trigonometry


【解决方案1】:

Numpy 有自己的sin 函数,可以高效地完成你想做的任务。效率低下的主要原因是求幂2 ** (-n * factor)

但是,numpy 在其exp 函数中确实具有有效的幂运算。所以我们可以将基数转换为e 以使用exp 通过使用

exp(-n * factor * log(2))

log 是另一个 numpy 函数,使用基数 e。您可以通过在 numpy 在其矢量化中设置的循环之外进行尽可能多的计算来进一步加快代码速度。换句话说,你首先设置了标量

newfactor = -np.log(2) * factor

并使用x = np.linspace(0, 10, 1000000) 或类似的东西设置您的x 数组。然后你的 y 数组是用

创建的
y = np.sin(x) * np.exp(newfactor * x)

现在y 是一个数组,其中所有计算值对应于x 数组中的值。

Numpy 自己进行循环,对于当前技术来说非常有效。我的实验表明,以这种方式进行幂运算所需的时间不到 np.power(2, -factor * x)2 ** (-x * factor)np.exp2(-x * factor) 所用时间的 1/5。查看整个表达式,对于x 一个长度为一百万的数组,我的代码总共需要29.2 ms 来执行。 @ComplicatedPhenomenon 的代码,看起来确实不错,总共需要 81.3 ms 执行,几乎是原来的 3 倍。 (感谢@ComplicatedPhenomenon 指出我的代码中的一个错误——我已经纠正了它,现在它似乎运行良好。)

【讨论】:

  • 我明白,看起来不错。不过,我想知道一件事:创建 2 个可能非常大的数组,只在最后生成 1 个数组不是很浪费吗? Numpy 是否提供了一种更有效的方法来做到这一点而无需创建冗余数组?
  • 我不确定 numpy 如何在内部工作。我确实知道它使用了大量的“矢量化”,它以适合现代 CPU、缓存和内存的顺序计算事物。据我所知,我的代码中的dotsinexp 可能不会单独执行,但可以通过x 数组一次合并,这样就没有多余的数组了。一个 numpy 专家可以说得更好。
  • @RoryDaulton 感谢您的详细解释。你注意到y最后是一个数字,而不是数字列表吗?
  • @ComplicatedPhenomenon:哎呀,你是对的。谢谢你的提示!我试图改进我的代码,但我记错了 np.dot 的作用。我已经删除了np.dot 并检查了我的代码,它现在可以工作了,即使速度有点慢。但正确比快速更重要!
【解决方案2】:

你可以使用 Numexpr

Numpy 对 sin(array), exp(array),.. 等简单操作有高效的实现。问题是每个表达式 (sin(n); -n * factor, 2 ** previous_Temp_array) 都是使用临时数组独立完成的。这会导致相当高的内存占用,并对性能产生负面影响。

代码

import numpy as np
import numexpr as ne

def orig(n_max,factor):
    n=np.arange(n_max)
    return np.sin(n) * 2 ** (-n * factor)

#Rory Daulton's solution
def mod(n_max,factor):
    n=np.arange(n_max)
    newfactor = -np.log(2) * factor
    return np.sin(n) * np.exp(newfactor * n)

def mod_2(n_max,factor):
    n=np.arange(n_max)
    return ne.evaluate("sin(n) * 2**(-n * factor)")

#Rory Daulton's solution using Numexpr
def mod_3(n_max,factor):
    n=np.arange(n_max)
    newfactor = -np.log(2) * factor
    return ne.evaluate("sin(n) * exp(newfactor * n)")

时间

%timeit res=orig(1e6,0.5)
81 ms ± 4.75 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit res=mod(1e6,0.5)
46.3 ms ± 5.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit res=mod_2(1e6,0.5)
16 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit res=mod_3(1e6,0.5)
11.1 ms ± 48.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【讨论】:

  • 这确实是一个很好的答案,尽管提问者暗示他/她想使用numpy,而这个答案还使用了另一个模块numexpr。但是,numexpr 相当普遍——例如,它包含并安装在 Python 的 Anaconda 发行版中。我认为这是最好的答案,比我的要好,所以 +1!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
相关资源
最近更新 更多