快速幂的计算和使用

今天在刷洛谷的时候,正巧发现在普及题库中的p1045非常的有意思,可以从某种意义上相似于大数相加,但经过仔细思考便发现并不是这样简单的情况,这个题目要求的如果考虑的太少的话会导致循环中的次数太多,从而不可以很好的解决,只能花费大量的时间和空间进行一步一步的运算,大概估计便可以得到这个一定会导致结果的失败,那么不如利用其他方法

快速幂的运算是在这种情况下比较好的一种情况。

正如大家所知道的,在a进行自我的平方运算的时候,自乘的结果可以在自乘n次的情况下得到a的二的n次方,同时也有a的x次方和a的y次方进行运算,可以得到a的x+y次方。

那么就有个例子,假如是a的12次方,设b=12,那么便可以得到12的十进制转化二进制可以得到二进制的1100。那么从左到右便可以认为是a的十二次方等同于a的八次方加上a的四次方,

那么便可以得到准备答案。

在这个过程当中,我们要做到在计算的时候实现次数的减少

在网上存在一个简单的计算

while(b > 0)
{
if(b & 1)
	ans *= base;

/*关于 b & 1:
“&”美名曰“按位与”。
x & y 是二进制 x 和 y 的每一位分别进行“与运算”的结果。
与运算,即两者都为 1 时才会返回 1,否则返回 0。
那么 b & 1

          二进制
b     =    1011
1     =    0001
b&1   =    0001

因为 1(二进制)的前面几位全部都是 0,
所以只有 b 二进制最后一位是 1 时,b & 1 才会返回 1。
挺巧妙的,并且很快。)*/
base *= base;
b >>= 1;
}

我写的相比这个会更麻烦一些, 

#include<iostream>//求a的b次方
using namespace std;
void dectobin(int b)
{
    int sum = 0;
    int y, x = 1; // y表示余数,x为叠加的系数
    while (b != 0)
    {
        y = b % 2;
        sum += x * y;
        x *= 10;
        b /= 2;
    }
    b=sum;
}
int main(){
    int a,b;
    double n=1;
    cin>>a>>b;
    dectobin(b);
    while(b>0){
        if(b&1){
            n *=a;
        }
        a *=a;
        b>>=1;
    }
    return n;
}

 但相同的可以解决这个问题,只不过需要稍微对这个代码进行修饰

便可以得到这样的结果

#include<iostream>
using namespace std;
typedef long long ll;
int main()
{
	ll b,p,k,ans;
	cin>>b>>p>>k;
	ans=1;
	cout<<b<<"^"<<p<<" mod "<<k<<"=";
	while(p > 0){
		if(p & 1)//b的二进制末尾为1,即奇数
			ans = ans * b % k;
		b = b * b % k;
		p >>= 1;//将b的二进制向右移1位数
	}
	cout<<ans%k;
	return 0;
}

可以说是很简单的解决方法,那么再利用这个先通过p1226

然后可以利用这个代码解决p1045的问题, 观察题目他给的数据十分简单,只有简单的几个要求

文件中只包含一个整数PP(1000<P<31000001000<P<3100000)

输出格式

第一行:十进制高精度数2^{P}-12P−1的位数。

第2-11行:十进制高精度数2^{P}-12P−1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0)

不必验证2^{P}-12P−1与PP是否为素数。

 

这个题说的是麦森书但在输出格式上并没有何其有太大的关系感觉。我在这里可以用更简单的方法对这个进行更改。

在第一个要求输出的在题解中有一个大神给出了很简洁的解决方法

s=n*log10(2)+1;用函数算位数 
cout<<s<<endl;

这种行为感觉真的是大神。。。

那么接下来便是对于第二问的解决

 

  while(n>=20){
        k=0;
        for(i=1;i<=50&&i<=top;i++){//数组的每一个元素里放十位数 
            a[i]=(a[i])<<20+k;//每次乘上2的20次方:1048576 把longlong剩余9位用到位 
            k=a[i]/10000000000;a[i]%=10000000000;//进位 
            if(top<50&&k&&i==top)top++;//加位数 ,前面s算过了 可以省 
        }
        n-=20;//一次算20个 
    }
    while(n){//把20个以下的依次算完 
        k=0;
        for(i=1;i<=50&&i<=top;i++){
            a[i]=a[i]<<1+k;
            k=a[i]/10000000000;a[i]%=10000000000;//用法同上 
            if(top<50&&k&&i==top)top++;
        }
        n--;
    }
    a[1]--;
    if(top<50){
        i=top+1;//可以用s 
        while(i<=50){//小于500位,要加前导零 
            for(j=1;j<=5&&i<=50;i++,j++)cout<<"0000000000";
            if(j==6)cout<<endl;
        }
        i=top;
        for(;j<=5&&i>=1;i--,j++){//注意:50位一行!!!j<=5!!! 
            if(a[i]>=1000000000)cout<<a[i];
            else{
                s=a[i];
                while(s>0){s/=10;o++;}
                o=10-o;
                while(o>0){o--;cout<<"0";}
                cout<<a[i];
            }
        }
        cout<<endl;
        while(i>=1){//注意:大于五百位也有可能有前导零 
            for(j=1;j<=5&&i>=1;i--,j++){//是每一个元素(10位)中的的前导零
                if(a[i]>1000000000)cout<<a[i];//判断是否有前导零 
                else{
                    s=a[i];
                    while(s>0){s/=10;o++;}//记录前导零个数 
                    o=10-o;
                    while(o>0){o--;cout<<"0";}//输出 
                    cout<<a[i];
                }
            }
            cout<<endl;
        }
    }
    else{
        s=a[50];
        i=50;
        while(i>=1){
            for(j=1;j<=5&&i>=1;i--,j++){
                if(a[i]>1000000000)cout<<a[i];
                else{
                    s=a[i];
                    while(s>0){s/=10;o++;}//      这一段用法同上 
                    o=10-o;
                    while(o>0){o--;cout<<"0";}
                    cout<<a[i];
                }
            }
            cout<<endl;
        }
    }
    return 0;
}

这个是大神的代码,但我现在使用的是快速幂,也可以稍微简单的解决

#include<bits/stdc++.h>

const long long mod=10000000000;

using namespace std;

const int N=1e5+10;

int P, l=1,lb=1;

int a[N]= {},b[N]= {},c[N]={};

int cheng1() {
    memset(c,0,sizeof(c));
    for (int i = 1; i <= l; ++i)   {
        for (int j = 1; j <= lb; ++j) {
            c[i+j-1] += a[i] * b[j];
            c[i+j] += ( c[i+j-1] ) / 10;
            c[i+j-1] %= 10;
        }
    }
    int lc = l + lb;
    while( c[lc] == 0 ) -- lc;
    for(int i = 1;i <= lc; ++i){
        a[i] = c[i];
    }
    return lc;
}
int cheng2() {
    memset(c,0,sizeof(c));
    for (int i = 1; i <= lb; ++i)   {
        for (int j = 1; j <= lb; ++j) {
            c[i+j-1] += b[i] * b[j];
            c[i+j] += ( c[i+j-1] ) / 10;
            c[i+j-1] %= 10;
        }
    }
    int lc = lb + lb;
    while( c[lc] == 0 ) -- lc;
    for(int i = 1;i <= lc; ++i){
        b[i] = c[i];
    }
    return lc;
}

int main() {

    memset( a, 0, sizeof(a));
    memset( a, 0, sizeof(b));
    scanf("%d",&P);
    a[1] = 1;
    b[1] = 2;
    -- a[1];
    for (int i = 500; i >= 1; --i) {
        printf("%d",a[i]);
        if ( ! ( (i-1) % 50 ) ) printf("\n");
    }
    return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值