算法篇——快速幂问题

107 阅读5分钟

内容五 快速幂问题

题目描述:

输入正整数𝑥, 𝑝 (𝑥 ≤ 10^9 , 𝑝 ≤ 10^18)

要求输出 xpmod(109+7)x^p mod(10^9+7)

样例输入 3 5

样例输出 243

拓展1

基本数据类型的特点,最大值和最小值。 1、
基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE= -2147483648 (-2的31次方)
最大值:Integer.MAX_VALUE= 2147483647 (2的31次方-1)
2、
基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768 (-2的15此方)
最大值:Short.MAX_VALUE=32767 (2的15次方-1)
3、
基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808 (-2的63次方)
最大值:Long.MAX_VALUE=9223372036854775807 (2的63次方-1)

4、

基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45 (2的-149次方)
最大值:Float.MAX_VALUE=3.4028235E38 (2的128次方-1)
5、
基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324 (2的-1074次方)
最大值:Double.MAX_VALUE=1.7976931348623157E308 (2的1024次方-1)

int和long只能写10个数字,short只能写5个数字,多了就会报错。

float的小数点后6位,double的小数点后16位。

拓展2

参考:www.geeksforgeeks.org/modulo-1097…

为什么需要取模:

  • 采用Mod的原因是为了防止整数溢出。C / C ++中最大的整数数据类型是64位无符号long long int,可以处理从0到(2 ^ 64 – 1)的整数。但是在某些问题中,产出增长率非常高,这种高范围的无符号长久可能不足。 假设在64位变量'A'中存储2 ^ 62,在另一个64位变量'B'中存储2 ^ 63。当我们将A和B相乘时,系统不会给出运行时错误或异常。它只是进行一些伪计算并存储伪结果,因为结果的位大小是在乘法溢出后得出的。

  • 在某些问题中,需要计算模逆的结果,并且该数字很有用,因为它是质数。同样,这个数字应该足够大,否则在某些情况下模块化逆向技术可能会失败。

由于这些原因,问题解决者需要通过对一些数N取模来给出答案。 N的值取决于某些标准:

  1. 它应该足够大以适合最大的整数数据类型,即确保没有结果溢出。
  2. 它应该是质数,因为如果我们将一个数的mod乘以质数,则结果通常是隔开的,即与非质数的mod数相比,结果是非常不同的结果,这就是质数通常用于mod的原因。

10 ^ 9 + 7满足这两个条件。它是第一个10位数的质数,也适合int数据类型。实际上,任何小于2 ^ 30的质数都可以,以防止可能的溢出。

模数的使用方式:模数 的一些分布特性如下:

  1. (a + b)%c =((a%c)+(b%c))%c
  2. (a * b)%c =((a%c)*(b%c))%c
  3. (a – b)%c =((a%c)–(b%c))%c
  4. (a / b)%c =((a%c)/(b%c))%c

因此,模是分布在+,*和–上,而不是分布在/上。[有关详细信息,请参阅模数除法] 注意:(a%b)的结果将始终小于b。 在计算机程序的情况下,由于变量限制的大小,我们**在每个中间阶段执行模M,**这样就不会发生范围溢出。

例:
一个= 145785635595363569532135132
b = 3151635135413512165131321321
c = 999874455222222200651351351
m = 1000000007
打印(a * b * c)%m。

方法1:
首先,将所有数字相乘,然后取模:
(a * b * c)%m =(459405448184212290290339339835148809
515332440033400818566717735644307024625348601572)% 
1000000007
a * b * c即使在unsigned long long中也不适合 
int由于哪个系统掉了大部分 
有效数字。因此,它给出了错误的答案。
(a * b * c)%m = 798848767

方法2:
在每个中间步骤取模:
我= 1
i =(i * a)%m // i = 508086243
i =(i * b)%m // i = 144702857
i =(i * c)%m // i = 798848767
我= 798848767 

方法2总是给出正确的答案。

拓展3

从效率上看,使用移位指令有更高的效率,因为移位指令占2个机器周期,而乘除法指令占4个机器周期。从硬件上看,移位对硬件更容易实现,所以会用移位,移一位就乘2,这种乘法当然考虑移位了。

位移运算:www.jianshu.com/p/927009730…

拓展4

快速幂问题:oi-wiki.org/math/quick-…

image-20201015093940552image-20201015094014653

思路:计算xpx^p与mod同时操作

import java.util.Scanner;

/**
 * @author SJ
 * @date 2020/10/15
 */
public class CalculateMod {
    public static final int MODULE = 1000000007;

    //x^p  x是int,p的范围是long
    public static long calculate(long x, long p) {
        x = Math.floorMod(x, MODULE);
        long res = 1L;
        for (; p != 0; p >>= 1) {
            //判断改二进制位上的数组是否为1 ,如过为1就乘上系数
            if ((p & 1) != 0)
                res = Math.floorMod(res * x, MODULE);
            //不为1就自乘
            x = x * x;
            x = Math.floorMod(x, MODULE);
        }
        return res;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        long x = scanner.nextLong();
        long p = scanner.nextLong();
        long calculate = calculate(x, p);
        System.out.println(calculate);

    }
}

结果:

"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe"...
 3 5
243

Process finished with exit code 0

算法复杂度,我们只需要计算O(logn)O(logn)次乘法,空间O(1)O(1)