快速幂-demo


问题描述

本文主要给出快速幂以及快速幂结合求模操作的一般方法

思路

当幂的数字太大时,计算的次数较大。
需要简化计算,可以采用快速幂。
二分幂的办法。

下面这是一种思路:
当b较大时,将其进行分解。

1. b为偶数时,a^b = a^(b/2) * a^(b/2)
2. b为奇数时,a^b = a^(b/2) * a^(b/2) * a

可用递归实现。
如果用非递归可在递归的基础上进行修改。但是要小心奇数和偶数的区别。
所以,还是要借助一个栈来实现。效率并不高。

下面讲另外一种思路:
主要思路是:
将a^b分解为若干个a^(2^k)项的乘积,也体现了分治的思想。
具体来说是在指数层面,将b分解为若干项(2^k)的和。

比如下面的例子:
3 ^ 999 = (3 ^ 512) * (3 ^ 256) * (3 ^ 128) * (3 ^ 64) * (3 ^ 32) * (3 ^ 4) * (3 ^ 2) * 3
= 3 ^ (512 + 256 + 128 + 64 + 32 + 4 + 2 + 1)

对于指数b的分解本质是求b的二进制形式
之所以写成二进制形式的好处是,前一位都可以由后一位的乘积构成。注意这里已经不是在指数层面了。
怎么判断哪一位的权出现,就看分解后相应二进制位是否有效。

看下面的问题: [ jobdu-1441]

题目描述:
求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

方法一

 int fast_pow( int a, int b, int mod )
{
    if(1==b)
        return a%mod;
    else
    {
        int t = fast_pow( a, b/2, mod );
        if( b%2 )
            return ((t%mod)*(t%mod)*(a%mod))%mod;
        else
            return ((t%mod)*(t%mod))%mod;
    }
}
int fast_pow_without_recursion( int a, int b, int mod )
{
    std::stack<int> stk;
    while( b > 1 )
    {
        stk.push(b%2);
        b /= 2;
    }

    int ans = a%mod;
    while( !stk.empty() )
    {
        int t = stk.top();
        stk.pop();

        if(t%2)
            ans = (ans%mod * ans%mod * a%mod)%mod;
        else
            ans = (ans%mod * ans%mod)%mod ;
    }
    return ans;
}

方法二

int fast_pow1( int a, int b, int mod )
{
    int ans = 1;
    int w = a;
    while( b )
    {
        if(b%2)
            ans = (ans * w%mod)%mod;
        w *= w;
        b /= 2;
    }
    return ans;
}

这个方法,看起来逻辑并没有问题。但是在调试的时候最后一组数据没有过。

1. 边界数据的问题
2. 中间值w*w过大

仔细分析后发现,由于每一个中间项都是由前一项平方得来。所以,加载这一项的求mod。
可以移到前面去。并且,最后一组数据爆了的原因就是,a很大之后w*w很容易直接爆掉。
所以,把求mod放在这里是正确的。应该可以解决之前的问题。

int fast_pow2( int a, int b, int mod )
{
    int ans = 1;
    int w = a;
    while( b )
    {
        if(b%2)
            ans = (ans * w)%mod;
        w = (w%mod * w%mod)%mod;
        b /= 2;
    }
    return ans;
}

总结

刷题一定要有耐心,就像对于方法二。满怀信心,觉得逻辑没有任何错误。但是还是有问题。不能直接百度,一定要自己反复思考,别害怕浪费时间。出来混的早晚都是要还的。虽然最后一点想了好久,但是最后还是想出来了,现在看来也许是轻而易举的事,但是你没到那个程度肯定是不行的。有问题是好事,有问题就能提高。享受解决问题的过程,这就是游戏,看你怎么玩。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值