• 浅谈快速幂


    前言: 这份讲解只针对于C++新手,各位大佬请绕道,如有讲解错误的地方可留言给我,我会在第一时间回复并加以改正。

    引入:快速幂的定义:顾名思义,快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。

    铺垫: 因为在代码实现中要用到位运算,没有学过位运算的读者我在这里简单讲解一下:
    基本算术位运算:

    \(and\),&,如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。例:11&3 =1011 & 11(二进制数)=11(二进制数) = 3

    \(or\),|,如果两个相应的二进制位只要有一个是1,结果就是1;否则为0。
    例:11|2=1011 | 10(二进制数)=1011(二进制数) =11

    \(not\),~,这个在加法中用到
    x-y = x + ~y + 1
    所以~y = - y -1
    比如 ~11 = -11 -1 = -12

    异或\(xor\),^,两个相同的数会变成0,反之是1
    例:11 ^ 3=1011 ^ 11 (二进制数)= 1000 (二进制数)=8

    左移 :在二进制表示下把数字同时向左移动,地位以0填充,高位越界后舍弃。

    \[1<<n=2^n , n<<1=2n \]

    算术右移 :在二进制补码表示下把数字同时向右移动,高位以符号位填充,低位越界后舍弃。

    \[n>>1= \lfloor n/2.0 \rfloor \]

    算术右移等于除以2向下除整, \((-3)>>1=-2,3>>1=1\)
    值得一提的是,“整数/2”在C++中实现为“除以2向零取整”,\((-3)/2=-1,3/2=1\)。请读者自己尝试使用C++编译器编译运行类似的语句,检查运算结果。

    原理:比如我们要求 \(a\)\(b\) 次方对 \(p\) 取模的值,其中1<=\(a,b,p\)<=\(10^9\)
    相关题目:Poj1995 Raising Modulo Numbers(快速幂)
    Poj1995 Raising Modulo Numbers(快速幂)

    根据数学常识,每一个正整数可以唯一表示为若干指数不重复的2的次幂的和。也就是说,如果 \(b\) 在二进制表示下有\(k\) 位,其中第\(i(0<=i<k)\)位的数字是 \(c_i\)

    那么: \(b=c_k 2^{k-1}+c_{k-1} 2^{k-2}+...+c_0 2^0\)

    于是: \(a^b=a^{c_{k-1}*2^{k-1}}*a^{c_{k-2}*2^{k-2}}*...*a^{c_0*2^0}\)

    又因为:\(a^{2^i}=(a^{2^{i-1}})^2\)

    所以我们很容易通过 \(k\) 次递推求出每个乘积项,当 \(c_i=1\) 时,把该乘积项累积到答案中。b&1运算可以取出 \(b\) 在二进制表示下的最低位,而 \(b>>1\) 运算可以舍去最低位,在递推的过程中将二者结合,就可以遍历 \(b\) 在二进制表示下的所有数位 \(c_i\) 。整个算法的时间复杂度为\(O(log_2b)\)

    代码实现:

    int power(int a,int b,int p)
    {
        int ans=1%p;
        while(b)
        {
            if(b&1)ans=(long long)ans*a%p;
            a=(long long)a*a%p;
            b>>=1;
        }
        return ans;
    }
    

    在上面的代码片段中,我们通过“右移(>>)”“与(&)”运算的结合,遍历了 \(b\) 的二进制表示下的每一位。在循环到第 \(i\) 次时(从0开始计数),变量 \(a\) 中存储的是 \(a^{2^i}\) ,若 \(b\) 该位为1,则把此时的变量 \(b\) 累积到答案 \(ans\) 中。

    值得提醒的是,在C++语言中,两个数值执行算术运算时,以参与运算的最高数值类型作为基准,与保存结果的变量类型无关。换言之,虽然两个 \(32\) 位整数的乘积可能超过int类型的表示范围,但是 \(CPU\)只会用1个32位寄存器保存结果,造成我们常说的越界现象。因此,我们必须把其中一个数强制转换成64位整数类型 long long 参与运算,从而得到正确的结果。最终对 \(p\) 取模以后,执行赋值操作时,该结果会被隐式转换成 \(int\) 存回 \(ans\) 中。

  • 相关阅读:
    3、tensorflow变量运算,数学运算
    2、tensorflow 变量的初始化
    1、tensorflow 框架理解
    tensorflow 打印全部变量的一种方法
    0、tensorflow学习开始
    tensorflow 小记——如何对张量做任意行求和,得到新tensor(一种方法:列表生成式)
    SASRec 实践
    jupyterlab 增加新内核的方法ipykernel
    vivo 全球商城:架构演进之路
    jenkins安装 git免密ssh配置
  • 原文地址:https://www.cnblogs.com/Luvwgyx/p/8408993.html
Copyright © 2020-2023  润新知