用C语言求一个整数的幂的最有效方法是什么?

// 2^3
pow(2,3) == 8

// 5^5
pow(5,5) == 3125

当前回答

如果要取2的a次方。最快的方法是按幂位移位。

2 ** 3 == 1 << 3 == 8
2 ** 30 == 1 << 30 == 1073741824 (A Gigabyte)

其他回答

这是对平方求幂效率的后续讨论。

这种方法的优点是它在log(n)时间内运行。例如,如果你要计算一个巨大的数,比如x^1048575(2^20 - 1),你只需要循环20次,而不是使用朴素方法的100万+次。

此外,在代码复杂性方面,它比试图找到最优的乘法序列更简单,这是la Pramod的建议。

编辑:

我想我应该在有人指责我可能会溢出之前澄清一下。这种方法假设您有某种巨大的int库。

请注意,平方求幂并不是最优的方法。这可能是一种适用于所有指数值的通用方法,但对于特定的指数值,可能有更好的序列,需要更少的乘法。

例如,如果你想计算x^15,用平方求幂的方法会给你:

x^15 = (x^7)*(x^7)*x 
x^7 = (x^3)*(x^3)*x 
x^3 = x*x*x

这一共有6次乘法。

事实证明,这可以通过“仅仅”5次加法链幂运算来完成。

n*n = n^2
n^2*n = n^3
n^3*n^3 = n^6
n^6*n^6 = n^12
n^12*n^3 = n^15

没有有效的算法来找到这个最优的乘法序列。从维基百科:

The problem of finding the shortest addition chain cannot be solved by dynamic programming, because it does not satisfy the assumption of optimal substructure. That is, it is not sufficient to decompose the power into smaller powers, each of which is computed minimally, since the addition chains for the smaller powers may be related (to share computations). For example, in the shortest addition chain for a¹⁵ above, the subproblem for a⁶ must be computed as (a³)² since a³ is re-used (as opposed to, say, a⁶ = a²(a²)², which also requires three multiplies).

我注意到gnu-GMP的标准指数平方算法有些奇怪:

我实现了两个几乎相同的函数——一个是幂模函数,使用最普通的二进制指数平方算法,

标签______2 ()

然后另一个基本相同的概念,但重新映射为每轮除以10,而不是除以2,

标签______10 ()

.

 ( time ( jot - 1456 9999999999 6671 | pvE0 | 

gawk -Mbe '
function ______10(_, __, ___, ____, _____, _______) {
      __ = +__
    ____ = (____+=_____=____^= \
           (_ %=___=+___)<_)+____++^____—

    while (__) {
        if (_______= __%____) {
            if (__==_______) {
                return (_^__ *_____) %___
            }
            __-=_______
            _____ = (_^_______*_____) %___
        }
        __/=____
        _ = _^____%___
    }
}
function ______2(_, __, ___, ____, _____) {
    __=+__
    ____+=____=_____^=(_%=___=+___)<_
    while (__) {
        if (__ %____) {
            if (__<____) {
                return (_*_____) %___
            }
            _____ = (_____*_) %___
            --__
        }
        __/=____
        _= (_*_) %___
    }
} 
BEGIN {
    OFMT = CONVFMT = "%.250g"

    __ = (___=_^= FS=OFS= "=")(_<_)

    _____ = __^(_=3)^--_ * ++_-(_+_)^_
    ______ = _^(_+_)-_ + _^!_

    _______ = int(______*_____)
    ________ = 10 ^ 5 + 1
    _________ = 8 ^ 4 * 2 - 1
}

GNU Awk 5.1.1, API: 3.1 (GNU MPFR 4.1.0, GNU MP 6.2.1)

.

($ + + NF = ______10(_ = ___美元,NR %________ +_________,_______*(_- 11))) ^ !___“

     out9: 48.4MiB 0:00:08 [6.02MiB/s] [6.02MiB/s] [ <=> ]
      in0: 15.6MiB 0:00:08 [1.95MiB/s] [1.95MiB/s] [ <=> ]
( jot - 1456 9999999999 6671 | pvE 0.1 in0 | gawk -Mbe ; )  

8.31s user 0.06s system 103% cpu 8.058 total
ffa16aa937b7beca66a173ccbf8e1e12  stdin

($ + + NF = ______ 2(_ = ___美元,NR %________ +_________,_______*(_- 11))) ^ !___“

     out9: 48.4MiB 0:00:12 [3.78MiB/s] [3.78MiB/s] [<=> ]
      in0: 15.6MiB 0:00:12 [1.22MiB/s] [1.22MiB/s] [ <=> ]
( jot - 1456 9999999999 6671 | pvE 0.1 in0 | gawk -Mbe ; )  

13.05s user 0.07s system 102% cpu 12.821 total
ffa16aa937b7beca66a173ccbf8e1e12  stdin

由于一些非常违反直觉和我不知道的原因,对于我投入的各种各样的输入,div-10变体几乎总是更快。这是两个哈希值之间的匹配,这让它真正令人困惑,尽管计算机显然没有内置在10进制的范例中。

我是否在代码/方法中遗漏了一些关键或明显的东西,可能会以令人困惑的方式歪曲结果?谢谢。

除了Elias的答案,当使用有符号整数实现时,会导致未定义行为,当使用无符号整数实现时,会导致高输入的不正确值,

下面是平方求幂的修改版本,它也适用于有符号整数类型,并且不会给出错误的值:

#include <stdint.h>

#define SQRT_INT64_MAX (INT64_C(0xB504F333))

int64_t alx_pow_s64 (int64_t base, uint8_t exp)
{
    int_fast64_t    base_;
    int_fast64_t    result;

    base_   = base;

    if (base_ == 1)
        return  1;
    if (!exp)
        return  1;
    if (!base_)
        return  0;

    result  = 1;
    if (exp & 1)
        result *= base_;
    exp >>= 1;
    while (exp) {
        if (base_ > SQRT_INT64_MAX)
            return  0;
        base_ *= base_;
        if (exp & 1)
            result *= base_;
        exp >>= 1;
    }

    return  result;
}

使用该函数的注意事项:

(1 ** N) == 1
(N ** 0) == 1
(0 ** 0) == 1
(0 ** N) == 0

如果将发生任何溢出或换行,则返回0;

I used int64_t, but any width (signed or unsigned) can be used with little modification. However, if you need to use a non-fixed-width integer type, you will need to change SQRT_INT64_MAX by (int)sqrt(INT_MAX) (in the case of using int) or something similar, which should be optimized, but it is uglier, and not a C constant expression. Also casting the result of sqrt() to an int is not very good because of floating point precission in case of a perfect square, but as I don't know of any implementation where INT_MAX -or the maximum of any type- is a perfect square, you can live with that.

int pow(int const x, unsigned const e) noexcept
{
  return !e ? 1 : 1 == e ? x : (e % 2 ? x : 1) * pow(x * x, e / 2);
  //return !e ? 1 : 1 == e ? x : (((x ^ 1) & -(e % 2)) ^ 1) * pow(x * x, e / 2);
}

是的,它是递归的,但是一个好的优化编译器会优化递归。