Many algorithms require to compute (-1)^n
(both integer), usually as a factor in a series. That is, a factor that is -1
for odd n and 1
for even n. In a C or C++ environment, one often sees:
许多算法需要计算(1)^ n(整数),通常是一系列的一个因素。也就是说,奇数n为-1,偶数n为1。在C或c++环境中,我们经常看到:
#include<iostream>
#include<cmath>
int main(){
int n = 13;
std::cout << std::pow(-1, n) << std::endl;
}
What is better or the usual convention? (or something else),
什么是更好的还是通常的约定?(或其他),
std::pow(-1, n)
std::pow(-1, n%2)
(n%2?-1:1)
(1-2*(n%2)) // (gives incorrect value for negative n)
EDIT: In addition, user @SeverinPappadeux proposed another alternative based on (a global?) array lookups. My version of it is:
编辑:此外,用户@SeverinPappadeux基于(全局?)数组查找提出了另一种选择。我的看法是:
const int res[] {-1, 1, -1}; // three elements are needed for negative modulo results
const int* const m1pow = res + 1;
...
m1pow[n%2]
This is not probably not going to settle the question but, by using the emitted code we can discard some options.
这可能不会解决问题,但是通过使用发出的代码,我们可以丢弃一些选项。
First without optimization, the final contenders are:
1 - ((n & 1) << 1);
(7 operation, no memory access)
(7操作,无内存访问)
mov eax, DWORD PTR [rbp-20]
add eax, eax
and eax, 2
mov edx, 1
sub edx, eax
mov eax, edx
mov DWORD PTR [rbp-16], eax
and
和
retvals[n&1];
(5 operations, memory --registers?-- access)
(5操作、内存寄存器?——访问)
mov eax, DWORD PTR [rbp-20]
and eax, 1
cdqe
mov eax, DWORD PTR main::retvals[0+rax*4]
mov DWORD PTR [rbp-8], eax
Now with optimization (-O3)
1 - ((n & 1) << 1);
(4 operation, no memory access)
(4操作,无内存访问)
add edx, edx mov ebp, 1 and edx, 2 sub ebp, edx
加上edx, edx mov ebp, 1和edx, 2下标ebp, edx
retvals[n&1];
(4 operations, memory --registers?-- access)
(4操作、内存寄存器?——访问)
mov eax, edx
and eax, 1
movsx rcx, eax
mov r12d, DWORD PTR main::retvals[0+rcx*4]
n%2?-1:1;
(4 operations, no memory access)
(4个操作,无内存访问)
cmp eax, 1
sbb ebx, ebx
and ebx, 2
sub ebx, 1
The test are here, . I had to some some acrobatics to have meaningful code that doesn't elide operations all together. https://godbolt.org/g/dD391u
考试就在这里。我不得不使用一些技巧来编写有意义的代码,这些代码不会忽略所有的操作。https://godbolt.org/g/dD391u
So at the end it depends on the level optimization and expressiveness:
所以最终它取决于水平的优化和表达性:
1 - ((n & 1) << 1);
is always good but not very expressive.
1 - (n & 1) < 1);总是很好,但不是很有表现力。
retvals[n&1];
pays a price for memory access.
retvals(n - 1);为内存访问支付代价。
n%2?-1:1;
is expressive and good but only with optimization.
n % 2 ? 1:1;有表现力和良好,但只有优化。
7 个解决方案
#1
47
You can use (n & 1)
instead of n % 2
and << 1
instead of * 2
if you want to be super-pedantic, er I mean optimized.
So the fastest way to compute in an 8086 processor is:
你可以使用(n & 1)而不是n % 2和<< 1而不是* 2,如果你想成为超级学究,我的意思是优化。8086处理器中最快的计算方式是:
1 - ((n & 1) << 1)
1 - (n & 1) < 1)
I just want to clarify where this answer is coming from. The original poster alfC did an excellent job of posting a lot of different ways to compute (-1)^n some being faster than others.
Nowadays with processors being as fast as they are and optimizing compilers being as good as they are we usually value readability over the slight (even negligible) improvements from shaving a few CPU cycles from an operation.
There was a time when one pass compilers ruled the earth and MUL operations were new and decadent; in those days a power of 2 operation was an invitation for gratuitous optimization.
我只是想澄清一下这个答案是从哪里来的。楼主alfC做了一个出色的帖子很多不同的方法来计算(1)^ n比其他人更快一些。现在,随着处理器的速度越来越快,优化编译器也越来越好,我们通常更重视可读性,而不是从操作中减少几个CPU周期(甚至可以忽略不计)的改进。曾经有一段时间,一个合格的编译器统治着地球,MUL操作是新的和颓废的;在那些日子里,2次行动的力量是一次无缘无故的优化。
#2
36
Usually you don't actually calculate (-1)^n
, instead you track the current sign (as a number being either -1
or 1
) and flip it every operation (sign = -sign
), do this as you handle your n
in order and you will get the same result.
通常你不计算(1)^ n,相反你跟踪当前签(作为一个数字1或1)和翻转每个操作(标志=‘),这样当你处理n,你会得到相同的结果。
EDIT: Note that part of the reason I recommend this is because there is rarely actually semantic value is the representation (-1)^n
it is merely a convenient method of flipping the sign between iterations.
编辑:注意,我推荐这个的一部分原因是因为很少有真正语义值是表示(1)^ n它仅仅是一个方便的翻法迭代之间的迹象。
#3
7
First of all, the fastest isOdd test I do know (in an inline method)
首先,我所知道的最快的isOdd测试(在内联方法中)
/**
* Return true if the value is odd
* @value the value to check
*/
inline bool isOdd(int value)
{
return (value & 1);
}
Then make use of this test to return -1 if odd, 1 otherwise (which is the actual output of (-1)^N )
然后利用这个测试返回1如果奇数,1否则((1)^ N)的实际输出
/**
* Return the computation of (-1)^N
* @n the N factor
*/
inline int minusOneToN(int n)
{
return isOdd(n)?-1:1;
}
Last as suggested @Guvante, you can spare a multiplication just flipping the sign of a value (avoiding using the minusOneToN function)
最后一个建议是@Guvante,你可以省去一个乘法运算,只需翻转一个值的符号(避免使用minusOneToN函数)
/**
* Example of the general usage. Avoids a useless multiplication
* @value The value to flip if it is odd
*/
inline int flipSignIfOdd(int value)
{
return isOdd(value)?-value:value;
}
#4
3
Many algorithms require to compute (-1)^n (both integer), usually as a factor in a series. That is, a factor that is -1 for odd n and 1 for even n.
许多算法需要计算(1)^ n(整数),通常是一系列的一个因素。也就是奇数n为-1偶数n为1。
Consider evaluating the series as a function of -x instead.
考虑将级数作为-x的函数进行评估。
#5
2
If it's speed you need, here goes ...
如果你需要的是速度,那么……
int inline minus_1_pow(int n) {
static const int retvals[] {1, -1};
return retvals[n&1];
}
The Visual C++ compiler with optimization turned to 11 compiles this down to two machine instructions, neither of which is a branch. It optimizes-away the retvals array also, so no cache misses.
Visual c++编译器将其优化为11,并将其编译为两个机器指令,这两个指令都不是分支。它还优化了retvals数组,因此不会出现缓存丢失。
#6
1
What about
是什么
(1 - (n%2)) - (n%2)
n%2
most likely will be computed only once
n%2极有可能只计算一次
UPDATE
更新
Actually, simplest and most correct way would be using table
实际上,最简单和最正确的方法是使用表。
const int res[] {-1, 1, -1};
return res[n%2 + 1];
#7
0
Well if we are performing the calculation in a series, why not handle the calculation in a positive loop and a negative loop, skipping the evaluation completely?
如果我们在一个系列中进行计算,为什么不在一个正循环和一个负循环中进行计算,完全跳过计算呢?
The Taylor series expansion to approximate the natural log of (1+x) is a perfect example of this type of problem. Each term has (-1)^(n+1), or (1)^(n-1). There is no need to calculate this factor. You can "slice" the problem by either executing 1 loop for every two terms, or two loops, one for the odd terms and one for the even terms.
近似(1+x)的自然对数的泰勒级数展开式就是这类问题的一个很好的例子。每个学期都有(1)^(n + 1),或(1)^(n - 1)。没有必要计算这个因子。你可以通过对每两项执行一个循环来“分割”问题,也可以对两个循环执行一个循环,一个是奇数项,一个是偶数项。
Of course, since the calculation, by its nature, is one over the domain of real numbers, you will be using a floating point processor to evaluate the individual terms anyway. Once you have decided to do that, you should just use the library implementation for the natural logarithm. But if for some reason, you decide not to, it will certainly be faster, but not by much, not to waste cycles calculating the value of -1 to the nth power.
当然,由于计算本质上是大于实数域的,所以无论如何,您都将使用浮点处理器来计算单个项。一旦您决定这样做,您应该使用库实现自然对数。但如果出于某种原因,你决定不这样做,它肯定会更快,但不是很多,不会浪费周期计算-1到n次方的值。
Perhaps each can even be done in separate threads. Maybe the problem can be vectorized, even.
也许每一个都可以在单独的线程中完成。也许这个问题可以向量化。
#1
47
You can use (n & 1)
instead of n % 2
and << 1
instead of * 2
if you want to be super-pedantic, er I mean optimized.
So the fastest way to compute in an 8086 processor is:
你可以使用(n & 1)而不是n % 2和<< 1而不是* 2,如果你想成为超级学究,我的意思是优化。8086处理器中最快的计算方式是:
1 - ((n & 1) << 1)
1 - (n & 1) < 1)
I just want to clarify where this answer is coming from. The original poster alfC did an excellent job of posting a lot of different ways to compute (-1)^n some being faster than others.
Nowadays with processors being as fast as they are and optimizing compilers being as good as they are we usually value readability over the slight (even negligible) improvements from shaving a few CPU cycles from an operation.
There was a time when one pass compilers ruled the earth and MUL operations were new and decadent; in those days a power of 2 operation was an invitation for gratuitous optimization.
我只是想澄清一下这个答案是从哪里来的。楼主alfC做了一个出色的帖子很多不同的方法来计算(1)^ n比其他人更快一些。现在,随着处理器的速度越来越快,优化编译器也越来越好,我们通常更重视可读性,而不是从操作中减少几个CPU周期(甚至可以忽略不计)的改进。曾经有一段时间,一个合格的编译器统治着地球,MUL操作是新的和颓废的;在那些日子里,2次行动的力量是一次无缘无故的优化。
#2
36
Usually you don't actually calculate (-1)^n
, instead you track the current sign (as a number being either -1
or 1
) and flip it every operation (sign = -sign
), do this as you handle your n
in order and you will get the same result.
通常你不计算(1)^ n,相反你跟踪当前签(作为一个数字1或1)和翻转每个操作(标志=‘),这样当你处理n,你会得到相同的结果。
EDIT: Note that part of the reason I recommend this is because there is rarely actually semantic value is the representation (-1)^n
it is merely a convenient method of flipping the sign between iterations.
编辑:注意,我推荐这个的一部分原因是因为很少有真正语义值是表示(1)^ n它仅仅是一个方便的翻法迭代之间的迹象。
#3
7
First of all, the fastest isOdd test I do know (in an inline method)
首先,我所知道的最快的isOdd测试(在内联方法中)
/**
* Return true if the value is odd
* @value the value to check
*/
inline bool isOdd(int value)
{
return (value & 1);
}
Then make use of this test to return -1 if odd, 1 otherwise (which is the actual output of (-1)^N )
然后利用这个测试返回1如果奇数,1否则((1)^ N)的实际输出
/**
* Return the computation of (-1)^N
* @n the N factor
*/
inline int minusOneToN(int n)
{
return isOdd(n)?-1:1;
}
Last as suggested @Guvante, you can spare a multiplication just flipping the sign of a value (avoiding using the minusOneToN function)
最后一个建议是@Guvante,你可以省去一个乘法运算,只需翻转一个值的符号(避免使用minusOneToN函数)
/**
* Example of the general usage. Avoids a useless multiplication
* @value The value to flip if it is odd
*/
inline int flipSignIfOdd(int value)
{
return isOdd(value)?-value:value;
}
#4
3
Many algorithms require to compute (-1)^n (both integer), usually as a factor in a series. That is, a factor that is -1 for odd n and 1 for even n.
许多算法需要计算(1)^ n(整数),通常是一系列的一个因素。也就是奇数n为-1偶数n为1。
Consider evaluating the series as a function of -x instead.
考虑将级数作为-x的函数进行评估。
#5
2
If it's speed you need, here goes ...
如果你需要的是速度,那么……
int inline minus_1_pow(int n) {
static const int retvals[] {1, -1};
return retvals[n&1];
}
The Visual C++ compiler with optimization turned to 11 compiles this down to two machine instructions, neither of which is a branch. It optimizes-away the retvals array also, so no cache misses.
Visual c++编译器将其优化为11,并将其编译为两个机器指令,这两个指令都不是分支。它还优化了retvals数组,因此不会出现缓存丢失。
#6
1
What about
是什么
(1 - (n%2)) - (n%2)
n%2
most likely will be computed only once
n%2极有可能只计算一次
UPDATE
更新
Actually, simplest and most correct way would be using table
实际上,最简单和最正确的方法是使用表。
const int res[] {-1, 1, -1};
return res[n%2 + 1];
#7
0
Well if we are performing the calculation in a series, why not handle the calculation in a positive loop and a negative loop, skipping the evaluation completely?
如果我们在一个系列中进行计算,为什么不在一个正循环和一个负循环中进行计算,完全跳过计算呢?
The Taylor series expansion to approximate the natural log of (1+x) is a perfect example of this type of problem. Each term has (-1)^(n+1), or (1)^(n-1). There is no need to calculate this factor. You can "slice" the problem by either executing 1 loop for every two terms, or two loops, one for the odd terms and one for the even terms.
近似(1+x)的自然对数的泰勒级数展开式就是这类问题的一个很好的例子。每个学期都有(1)^(n + 1),或(1)^(n - 1)。没有必要计算这个因子。你可以通过对每两项执行一个循环来“分割”问题,也可以对两个循环执行一个循环,一个是奇数项,一个是偶数项。
Of course, since the calculation, by its nature, is one over the domain of real numbers, you will be using a floating point processor to evaluate the individual terms anyway. Once you have decided to do that, you should just use the library implementation for the natural logarithm. But if for some reason, you decide not to, it will certainly be faster, but not by much, not to waste cycles calculating the value of -1 to the nth power.
当然,由于计算本质上是大于实数域的,所以无论如何,您都将使用浮点处理器来计算单个项。一旦您决定这样做,您应该使用库实现自然对数。但如果出于某种原因,你决定不这样做,它肯定会更快,但不是很多,不会浪费周期计算-1到n次方的值。
Perhaps each can even be done in separate threads. Maybe the problem can be vectorized, even.
也许每一个都可以在单独的线程中完成。也许这个问题可以向量化。