为啥求幂(例如 10^6)比 R 中的计算器符号(例如 1e6)花费的时间长 4 倍?
Posted
技术标签:
【中文标题】为啥求幂(例如 10^6)比 R 中的计算器符号(例如 1e6)花费的时间长 4 倍?【英文标题】:Why does exponentiation (e.g., 10^6) take 4 times longer than calculator notation (e.g., 1e6) in R?为什么求幂(例如 10^6)比 R 中的计算器符号(例如 1e6)花费的时间长 4 倍? 【发布时间】:2015-07-12 07:34:56 【问题描述】:在 R 代码中使用科学记数法 10^6
(我通常这样做)会导致比使用计算器表示 1e6
更长的计算时间:
> system.time(for (t in 1:1e7) x=10^6)
utilisateur système écoulé
4.792 0.000 4.281
> system.time(for (t in 1:1e7) x=1e6)
utilisateur système écoulé
0.804 0.000 1.051
> system.time(for (t in 1:1e7) x=exp(6*log(10)))
utilisateur système écoulé
6.301 0.000 5.702
为什么 R 重新计算 10^6
的时间与计算 exp6*log(10)
的时间差不多?我理解R在计算10^6
时会执行一个函数,但是为什么要这样编码呢?
【问题讨论】:
我猜一个是只需要翻译的数字文字,而另一个是首先需要评估的表达式。 你为什么认为R通过exp(6*log(10))
计算10^6
?
这不是您所说的“以特定方式编码”的问题,而是您明确告诉 R 计算 10 的 6 次方...例如,您宁愿分配 @ C++ 中的 987654329@ 或 double a=pow(10,6);
?
@cryo111:我理解原因,但对使用幂符号时的效率损失感到遗憾。这不是世界末日,但我在未来的代码中必须小心这一点。
@Xi'an BTW 一个好问题。此外,您还得到了两个不错的答案以及 MrFlick 和 Josh 的一些有趣的额外见解。 +1 全部
【参考方案1】:
这是因为1e6
是constant,并且被解析器识别为这样,而10^6
被解析为必须进一步评估的函数调用(通过调用函数^()
) .由于前者避免了函数调用的昂贵开销,因此评估它要快得多!
class(substitute(1e6))
# [1] "numeric"
class(substitute(10^6))
# [1] "call"
为了更好地看出这是一个电话,你可以这样剖析它:
as.list(substitute(10^6))
# [[1]]
# `^`
#
# [[2]]
# [1] 10
#
# [[3]]
# [1] 6
其他一些有趣的案例:
## negative numbers are actually parsed as function calls
class(substitute(-1))
[1] "call"
## when you want an integer, 'L' notation lets you avoid a function call
class(substitute(1000000L))
# [1] "integer"
class(substitute(as.integer(1000000)))
# [1] "call"
【讨论】:
非常有趣的附加案例!但令人费解。我以前从未听说过使用 L(但分配负数时似乎并没有节省时间) @Xi'an 是的,R 使用尾随L
来指示整数与将 -
视为函数调用不同。据我所知,如果不执行函数调用,根本无法提供负常数 --- 没有 "1N"
等。【参考方案2】:
在1e6
的情况下,您指定的是文字数值。那里没有进行计算。
当您请求10^6
时,R 会执行将 10 提高到 6 次方的工作。 ^
是一个数字运算符。它对 base 10 没有任何特殊作用。它不知道 10^6
和 12^14
之间的区别。它必须进行计算才能找到答案。
【讨论】:
谢谢。遗憾的是,10 没有获得特殊地位! 赋予 10 个特殊状态会使解析器复杂化。我显然没有做任何测试,但这很可能会比偶尔的功率计算更慢。以上是关于为啥求幂(例如 10^6)比 R 中的计算器符号(例如 1e6)花费的时间长 4 倍?的主要内容,如果未能解决你的问题,请参考以下文章