人见人爱 A ^ B - 九度教程第 57 题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LYKXHTP/article/details/88528950

人见人爱 A ^ B - 九度教程第 57 题

题目

时间限制:1 秒 内存限制:128 兆 特殊判题:否
题目描述:
A^B 的最后三位数表示的整数。说明:A^B 的含义是“A 的 B 次方”
输入:
输入数据包含多个测试实例,每个实例占一行,由两个正整数 A 和 B 组成 (1<=A,B<=10000),如果 A=0, B=0,则表示输入数据的结束,不做处理。
输出:
对于每个测试实例,请输出 A^B 的最后三位表示的整数,每个输出占一行。
样例输入:
2 3
12 6
6789 10000
0 0
样例输出:
8
984
1

分析题面可知先求得A^B的具体数字再求其后的三位数不可行,题面给明的输入规模,A^B的次至多可以达到10000的10000次,这么庞大的数字是非常不容易保存的,但注意到A^B的后三位数只与A的后三位数和B有关。这样,由于要求的仅是最后结果的后三位数,那么计算过程中的所有中间结果仅保存和使用其后三位数即可。所以本题主要需要利用二分求幂来求得 A^B 次,同时在计算中间结果时都仅保存后三位数,这样就不用担心数字不能被保存的问题了。

二分求幂,即怎样快速的求得 a 的 b 次方。实践中涉及这个问题的程序很可能使用了一个循环次数为 b 的 for 循环,并在每次循环时都累乘a,这样在 b 次循环结束时就将获得 a 的 b
次。如:

int ans=1;
for (int i = 1;i <= b;i ++) { 
	ans *= a; 
 }

假如将要计算 2 的 32 次,即 2^32。是否需要真的循环 32 次呢? 按照采用的原始策略,当循环到第 i 次时,此时的累乘结果即为 2 的 i 次,即 2^i。
那么,当完成了前 16 次循环时就已经获得了 2 的 16 次的数值,要获得 2 的 32 次还需要继续完成后续的 16 次循环么?答案是否定的,只需将 2 的 16 次对应的数字求平方即可计算出 2 的 32 次方,这样后 16 次乘法运算用一次平方(同样也是乘法)就完成了。
既然 2 的 32 次可以由 2 的 16 次求平方取得,那么2 的 16 次只需对2的8求平方即可,同理要求2的8次我们只需对2的4次求平方……
这样依次往复,最后可得到如下计算过程:
在这里插入图片描述
这样原本需要使用 32 次乘法才能完成的工作,现在只需要 6 次乘法便能完成,效率提高了将近 6 倍,这种求某个数的指定次幂的方法即二分求幂。
2 的 32 次具有某种特殊性,即32次刚好为 2 的 5 次方,所以其可以一直被二分到 1次方为止。假如要求的次数不再具有这种特殊性,二分求幂也能适用。以求2 的 31 次为例,继
续了解二分求幂的工作特点:

在这里插入图片描述
其中 2 的各次幂可以由之前讨论过的方法求得,即求得 2 的 1 次幂后,利用对其平方,求得 2 的 2 次幂,再对 2 的 2 次幂求平方即可求得 2 的 4 次幂,依次 类推。当求得这些 2 的各次幂后,只需按要求对其累乘,即可得到答案。
那么该如何确定哪些 2 的次幂是需要的,是要累乘的呢?首先,应该注意到从 a 的 1 次出发,a 的 2 次,a 的 4 次,a 的 8 次,即 a 的 2^k 次是可以由 a 的 1 次不断求平方取得的。目标即分解 a 的 b 次变为若干个 a 的 2^k 次的积,并尽可能减少分解结果的个数。在指数层面即分解 b 为若干个 2^k 的和, 并尽可能减少分解结果的个数。
联想到分解 b 为若干个 2^k 的和且分解个数最小,这便是求 b 的二进制数。在求得 b 的二进制数后,各个二进制位为 1 的数位所代表的权重即是分解的结果。以 2 的 31 次方为例,首先求得 31 的二进制数 11111,在二进制表达式中 31 即被表达成(11111) = 2^0 + 2^1 + 2^2 + 2^3 + 2^4,这就是所需的分解结果。即拆 2 的 31 次为 2 的 0 次、1 次、2 次、3 次、4 次的乘积,即得到前文中所讲的结果。

所以二分求幂对要求的次数并没有特殊的要求,而是对任何要求的次数都可以采用二分求幂来大大减少其乘法运算的次数。

#include <stdio.h>

int main()
{
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF){
        if(a==0 && b==0)break;
        int ans=1;//保存最终结果,初始值为1
        while(b!=0){//若b不为0,即对b转换二进制过程未结束
            if(b%2==1){
                //若当前二进制位为1,则需要累乘a的2^k次
                //至变量ans,其中2^k次为当前二进制位的权重
                ans*=a;//最终结果累乘a
                ans%=1000;//求其后三位数
            }
            b/=2;//b除以2
            a*=a;
            //求下一位二进制位的权重,a求其平方
            //即从a的1次开始,依次求a的2次,a的4次...

            a%=1000;//求a的后三位
        }//一边计算b的二进制值,一边计算a的2^k次,并将需要的部分累乘到变量ans上
        printf("%d\n",ans);
    }
    return 0;
}

模拟代码计算2^31的值,如下:
在这里插入图片描述
在这里插入图片描述
从 b 的最低位开始依次求得 b 的各二进制位,在当前二进制位为 1 的条件下将a累乘到变量ans上,在完成本位的操作后对a求其平方计算下一位二进制位的权重,直到完成对b的二进制转换。

猜你喜欢

转载自blog.csdn.net/LYKXHTP/article/details/88528950