算法1.快速幂【a^b、(a^b)%p】

算法1.快速幂( a b a^b ab a b % p a^b\%p ab%p)

快速幂是一个很常见的算法,该算法主要求解的是 a b a^b ab类型的运算,这样的运算往往数据类型会比较大,所以会导致越界,所以结果往往是按照取模给出,即 ( a b ) % c (a^b)\%c (ab)%c.在接触快速幂之前,首先要了解一个式子。即
a b % p = [ ( a % p ) ( b % p ) ] % p ab\%p = [(a\%p)(b\%p)]\%p ab%p=[(a%p)(b%p)]%p

有的人可能不太了解这个式子的由来,所以下面来推到以下这个式子。设 a = m p + i , b = n p + j a = mp + i, b = np + j a=mp+i,b=np+j,则有
a b % p = [ ( m p + i ) ( n p + j ) ] % p = [ m n p 2 + ( n i y + m j x ) p + i j ] % p = i j % p \begin{aligned} ab\%p &= [(mp + i)(np + j)] \% p \\& = [mnp^2 + (niy + mjx)p + ij] \% p \\& = ij \% p \end{aligned} ab%p=[(mp+i)(np+j)]%p=[mnp2+(niy+mjx)p+ij]%p=ij%p
又因为 a % p = i , b % p = j a \% p = i, b \% p = j a%p=i,b%p=j,所以可得
a b % p = [ ( a % p ) ( b % p ) ] % p = i j % p ab\%p = [(a\%p)(b\%p)]\%p=ij \% p ab%p=[(a%p)(b%p)]%p=ij%p

接下来看一下快速幂的思想,快速幂的思想就是将指数不断缩小分离出去。假如我们需要求解式子 3 23 3^{23} 323,接下里看看如何进行这些操作。会有以下的变换。
3 23 = 1 × 3 23 由于指数是奇数,所以分离出来一个底数因子 3 = ( 1 × 3 ) × 3 23 − 1 = 3 × 3 22 由于指数部分是偶数,所以底数平方,指数减半 = 3 × ( 3 2 ) 22 2 = 3 × 9 11 由于指数部分是奇数,需要分离出来一个底数因子 9 = ( 3 × 9 ) × 9 11 − 1 = 27 × 9 10 同上偶数,指数减半,底数平方 = 27 × 8 1 5 同上奇数,分离一个底数因子 = 2187 × 8 1 4 = 2187 × 656 1 2 = 2187 × 4304672 1 1 = 94143178827 × 4304672 1 0 = 94143178827 \begin{aligned} 3^{23} &= 1 \times 3^{23} \hspace{3em} 由于指数是奇数,所以分离出来一个底数因子3\\ &= (1\times 3) \times 3^{23 - 1} \\ &= 3 \times 3 ^ {22} \hspace{3em} 由于指数部分是偶数,所以底数平方,指数减半\\ &= 3 \times (3^2)^{\frac{22}{2}} \\ &= 3 \times 9^{11} \hspace{3em} 由于指数部分是奇数,需要分离出来一个底数因子9 \\ &= (3 \times 9) \times 9 ^ {11 - 1} \\ &= 27 \times 9^{10} \hspace{3em} 同上偶数,指数减半,底数平方\\ &= 27 \times 81^{5} \hspace{3em} 同上奇数,分离一个底数因子 \\ &= 2187 \times 81 ^ 4 \\ &= 2187 \times 6561 ^ 2 \\ &= 2187 \times 43046721 ^ 1 \\ &= 94143178827 \times 43046721^0\\ &= 94143178827 \end{aligned} 323=1×323由于指数是奇数,所以分离出来一个底数因子3=(1×3)×3231=3×322由于指数部分是偶数,所以底数平方,指数减半=3×(32)222=3×911由于指数部分是奇数,需要分离出来一个底数因子9=(3×9)×9111=27×910同上偶数,指数减半,底数平方=27×815同上奇数,分离一个底数因子=2187×814=2187×65612=2187×430467211=94143178827×430467210=94143178827
这大概就是一个快速幂的想法,就是不断的观察指数部分是否为奇数,如果是奇数就分离一个底数出去;否则是偶数则将底数进行平方操作,指数减半。看上述流程到0则结束操作。用C语言代码进行描述如下:

long long int fastpower(long a, long b)
{
    // 结果
    long long ans = 1;
    while(b != 0)
    {
        // 奇数,分离一个底数出来,指数减1
        if(b % 2 == 1) 
        {
            ans = (long long)ans * a;
            b -= 1;
        }
        // 偶数操作,底数平方,指数减半
        else 
        {
            a *= a;
            b /= 2;
        }
    }

    return ans;
}

由于对于偶数的时候b/2b>>2也是等效的;判断奇数偶数的条件也可以改为b&1,若该值为1则表示为奇数,否则为偶数。所以上述代码可以改进为以下形式。

long long int fastpower(long a, long b)
{
    // 结果
    long long ans = 1;
    while(b)
    {
        // 奇数,分离一个底数出来,指数减1
        if(b & 1) 
        {
            ans = (long long)ans * a;
            b -= 1;
        }
        // 偶数操作,底数平方,指数减半
        else  
        {
            a *= a;
            b >>= 1;
        }
    }

    return ans;
}

但是思考一下,若b是奇数,则执行完下一步操作一定是偶数,则其实奇数和偶数同时操作的时候可以直接等效于b>>=1。所以代码可以改进为:

long long int fastpower(long a, long b)
{
    // 结果
    long long ans = 1;
    while(b)
    {
        // 奇数,需要单独操作一次
        if(b & 1)  ans = (long long)ans * a;
        // 偶数操作,若奇数则下一步一定是偶数操作
        a *= a;
        b >>= 1;
    }

    return ans;
}

但是由于常见的快速幂的结果往往是需要对某个数字进行取模,所以加上最后的参数。最开始的式子其实经过推广可以得到:
a 1 a 2 ⋯ a n − 1 a n % p = [ ( a 1 % p ) ( a 2 % p ) . . . ( a n − 1 % p ) ( a n % p ) ] % p a_1a_2\cdots a_{n-1}a_n \% p = [(a_1\% p)(a_2\% p)...(a_{n-1}\% p)(a_n\% p)]\%p a1a2an1an%p=[(a1%p)(a2%p)...(an1%p)(an%p)]%p
a b a^b ab实际上就是 b b b a a a相乘,即:
a b % p = ( a × a × ⋯ × a × a ⏟ ) b % p = [ ( a % p ) ( a % p ) ⋯ ( a % p ) ( a % p ) ⏟ ] b % p \begin{aligned} a^b \% p &= \begin{matrix} (\underbrace{a \times a \times \cdots \times a \times a}) \\ b \end{matrix} \% p \\ &= \begin{matrix} [\underbrace{(a \% p)(a \%p) \cdots (a \% p)(a \% p)}] \\ b \end{matrix} \% p \end{aligned} ab%p=( a×a××a×a)b%p=[ (a%p)(a%p)(a%p)(a%p)]b%p

若加上系数则为:
k × a b % p = k × ( a × a × ⋯ × a × a ⏟ ) b % p = [ ( k % p ) ( a % p ) ( a % p ) ⋯ ( a % p ) ( a % p ) ⏟ ] b % p \begin{aligned} k \times a^b \% p &= \begin{matrix} k \times( &\underbrace{a \times a \times \cdots \times a \times a}) \\ &b \end{matrix} \% p \\ &= \begin{matrix} [(k \% p)&\underbrace{(a \% p)(a \%p) \cdots (a \% p)(a \% p)}] \\ &b \end{matrix} \% p \end{aligned} k×ab%p=k×( a×a××a×a)b%p=[(k%p) (a%p)(a%p)(a%p)(a%p)]b%p

而在程序中a *= a正好对应着后续的 b b b a a a,该行代码可以改为a = a * a % p。而ans = (long long)ans * a正好对应着加上系数的式子,因此可以改成为ans = (long long)ans * a % p。于是最终的快速幂代码如下:

long long int fastpower(long a, long b, long p)
{
    // 结果
    long long ans = 1;
    while(b)
    {
        // 奇数,需要单独操作一次
        if(b & 1)  ans = (long long)ans * a % p;
        // 偶数操作,若奇数则下一步一定是偶数操作
        a = a * a % p;
        b >>= 1;
    }

    return ans;
}

相关推荐

  1. 算法】数论---快速

    2024-07-18 05:54:03       57 阅读
  2. 快速算法详解

    2024-07-18 05:54:03       39 阅读
  3. 算法1.快速【a^b、(a^b)%p】

    2024-07-18 05:54:03       22 阅读
  4. 算法刷题day38:快速

    2024-07-18 05:54:03       27 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 05:54:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 05:54:03       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 05:54:03       58 阅读
  4. Python语言-面向对象

    2024-07-18 05:54:03       69 阅读

热门阅读

  1. 第三节SHELL脚本中的变量与运算(2.2)

    2024-07-18 05:54:03       19 阅读
  2. nng协议nni_posix_resolv_sysinit()系统初始化

    2024-07-18 05:54:03       22 阅读
  3. Tensor列表索引本质

    2024-07-18 05:54:03       20 阅读
  4. 社会科学战线

    2024-07-18 05:54:03       22 阅读
  5. 资源管理大师:如何在Gradle中配置资源目录

    2024-07-18 05:54:03       25 阅读
  6. derivate_gauss 将图像与高斯函数的导数卷积

    2024-07-18 05:54:03       22 阅读
  7. 掌握Xcode Storyboard:iOS UI设计的可视化之旅

    2024-07-18 05:54:03       21 阅读
  8. Anylogic中Excel 文件(Excel file)的使用

    2024-07-18 05:54:03       17 阅读
  9. uniapp动态计算并设置元素高度

    2024-07-18 05:54:03       22 阅读
  10. uniapp 解决scroll-view组件 refresher-triggered刷新无效

    2024-07-18 05:54:03       20 阅读
  11. AWS ECS 服务创建 CloudWatch 告警

    2024-07-18 05:54:03       19 阅读