【问题标题】:(-1)^n exponentiation in c++(-1)^n c++ 中的幂运算
【发布时间】:2013-01-05 23:34:33
【问题描述】:

考虑以下形式的收敛级数:

sum(((-1)^n)*something) 

其中n 是迭代索引(n1infinity)。

如果我们直接实现公式,我们有std::pow(-1, n),但有没有更“快速”的算法技巧来实现它?

【问题讨论】:

  • 如果pow 太慢,您始终可以使用布尔条件。
  • 您进行了基准测试吗?你怎么知道编译器不会自动优化掉指数?
  • 如何收敛?当n1 变为infinity 时,-something0 之间的总和不会闪烁吗?

标签: c++ math optimization


【解决方案1】:

检查n是偶数还是奇数,

(n % 2 == 0) ? 1 : -1;

做到了。如果你想避免一个分支,

1 - 2*(n & 1)

【讨论】:

  • 我很确定 (n & 1)?-1:1 是更快的方法。我担心 % 2 会使其实际除法(如果 n 不是无符号的),并且乘法很可能会导致真正的乘法,这在现代处理器上至少需要几个周期。 gcc 至少在没有分支的情况下整理三元表达式方面做得很好。
  • @MatsPetersson 如果编译器没有用& 1 替换% 2(在二进制补码机器上),就把它扔掉,它就是垃圾。如果在所讨论的平台上位移不是更快,则乘法应该只保留一。但我也不希望无分支代码更快。两者应该几乎相同。
  • gcc 执行 a and with one,但添加了另外 4 条指令来处理 n 可能为负数的事实。使其无符号[假设这是有效的],但它没有。 ` movl %edi, %eax shrl $31, %eax leal (%rdi,%rax), %esi andl $1, %esi subl %eax, %esi`(在评论中可能看起来很乱!)
  • @MatsPetersson 哦,四个字母的单词,确实如此。有一次我听了“写% 2,如果你想测试奇偶校验,编译器会整理出来”的劝告,我不应该这样做,并坚持本能。
  • @Tinctorius 我不能否认这一点。为了替换有符号整数的模运算,当然需要二进制补码。
【解决方案2】:

我假设sum(((-1)^n)*something) 是伪代码,n 是一个由 sum 绑定的变量。

让我们将该符号扩展为sum(n <- [0,1,2,3..], ((-1)^n)*f(n))。您最好的选择可能是首先将其分成两个总和,然后将它们加在一起:

sum(n <- [0,2..], ((-1)^n)*f(n)) + sum(n <- [1,3..], ((-1)^n)*f(n))

在第一个术语中,n 始终是偶数,因此 (-1)^n 将始终是 +1。类似地,在第二个任期内,它将始终是-1。我们现在可以改写如下:

sum(n <- [0,2..], f(n)) + sum(n <- [1,3..], -f(n))

由于第二个总和中的每一项都乘以一个常数,因此我们可以将该常数移出总和:

sum(n <- [0,2..], f(n)) - sum(n <- [1,3..], f(n))

现在,让我们确保这些总和采用相同的索引序列,并将2*m2*m+1 替换为n

sum(m <- [0,1..], f(2*m)) - sum(m <- [0,1..], f(2*m+1))

现在我们可以再次合并这些总和:

sum(m <- [0,1..], f(2*m) - f(2*m+1))

或者,如果你想要伪 C:

T result = 0;
for(m = 0; m < limit; m+=2) {
    result += f(m);
    result -= f(m+1);
}

这为您节省了乘以 +1-1,正如大多数人似乎在这里建议的那样。由于您的序列是收敛的,因此多取一项不会对答案的正确性产生负面影响。

【讨论】:

    【解决方案3】:

    是的,有一个魔术:(-1)^n == 1 当且仅当n 是偶数,并且(-1)^n == -1 当且仅当n 是奇数。因此:

    int p = (n % 2 == 0) ? 1 : -1;
    sum(p*something)
    

    【讨论】:

      【解决方案4】:

      如果你在循环中这样做,你可以简单地这样做:

      x = 1;   // Assuming we start on n = 0
      for(...)    // or while(...)
      {
         sum += x * something;
      
         x = -x;
      }
      

      这很可能比对 n 进行检查要快得多 - 当然,它确实假设所有 n 值都被迭代,并且您不会在这里和那里跳过一些......

      【讨论】:

        【解决方案5】:

        术语((-1)^n)*something 计算为-something 表示奇数n,或something 表示偶数n

        n &amp; 1 ? -something : something

        如果something 是一个常数值,那么当n 的最后一个值为奇数时,sum(((-1)^n)*something) 的计算结果为-something,或者对于偶数个求和数,则计算结果为0

        n &amp; 1 ? -something : 0

        在这种情况下,serie 不会收敛。

        【讨论】:

        • 我的解释是“某事”不是一个常数。
        • 我的解释是,一般的开发者数学真的很差。
        • 然而,术语“收敛级数”确实暗示它不是一个恒定的东西,因为正如你所描述的那样,它只会在 0 和 - 某物之间振荡 - 这在我的数学书中不是收敛 - 但那大约在三十年前,所以也许“新数学”对收敛的解释不同。
        • @ft1:可以安全地假设 OP 的真正意思是 f(n) something。结合级数收敛的保证,函数f必须要么是恒定的0,要么根本不是恒定的。
        • 嘿,@MatsPetersson 和 ft1,你们说的是同一件事。一种是说“如果某物是常数,则级数不收敛”,另一种是说“如果级数收敛,则某物不是常数”。而且(根据我更早的数学书)这两个陈述是等价的。
        猜你喜欢
        • 2018-07-18
        • 2017-02-02
        • 1970-01-01
        • 2011-01-13
        • 2017-07-28
        • 1970-01-01
        • 2011-09-18
        • 1970-01-01
        相关资源
        最近更新 更多