茶话会
终于回到了正业上。不知道蓝桥杯啥时候第二阶段,反正接近一个月一直在数学建模数学建模,真的给我打吐了,时不时还有乱七八糟的小组作业来恶心我。数学建模到一定瓶颈很难突破下去,除了熟练写论文也没什么实际意义,至于一些别的比如创赛。。。懂的都懂,知识学不到一点。我真的有点佩服把这么深奥的底层逻辑用独立的一套语言表示出来的这种行为,你知道这是什么,但我也肯定你绝对不知道是什么。
还是算法好,A出来了的就是实打实的会了。。。
模幂运算
幂运算不必多说是什么。取模防止结果过大,每一步计算结果都要取模。
那现在的问题是,怎么“快速”呢?
比如2^15不是老老实实算15次就行吗?
错啦!我直接摆一个公式:2^15=2^8*2^4*2^2*2^1
而在这里,2^2可以由2^1算出来,2^4可以由2^2算出来,2^8可以由2^4算出来。
这样就避免了很多重复性的计算,这就是快速幂的核心思想。
而15的二进制表示为1111,那么2的1次方到4次方全员参与进来计算。
看完这些,如果你还不太理解,那么把15换成13(1101)
2^15=2^8*2^2*2^1,中间的2^4没得乘了,因此你的指数的二进制要控制它成不成本身。
总结一下流程:
计算a^b mod n,我们初始化一个ans=1,然后进入一个循环:
我们需要加一个判定,就是b要掌控你的结果乘不乘现在的a。
掌控方式就是b%2==1,也就是现在的b对2取余为几。
底数a再自乘,也就是a=a*a;
b再除2赋给自己。
经常沉迷在洛谷力扣等地方的同学可以发现,b%2输出一个结果,再进行b/=2,这样循环,恰好就是b的二进制表示的倒过来的结果。这也正是我们希望的a指数级增长的意图。
如果你还不懂:
取a=2 b=13 n=10086(你在理解这段的时候就先别管模运算了),赋ans=1。
判断条件 | ans | a | b |
13%2==1 | 2^1 | 2^2 | 6 |
6%2==0 | 2^1 | 2^4 | 3 |
3%2==1 | 2^1*2^4 | 2^8 | 1 |
1%2==1 | 2^1*2^4*2^8 | 2^16 | 0 |
纯保姆级教学啊,快点个赞!
代码
//现在写的
#include<iostream>
using namespace std;
long long a,b,n;
long long quickpower();
int main()
{
cin>>a>>b>>n;
cout<<quickpower();
return 0;
}
long long quickpower()
{
long long ans=1;
while(b!=0){
if(b%2==1)
ans=ans*a%n;
a=a*a%n;
b/=2;
//cout<<ans<<' '<<a<<' '<<b<<endl;
}
return ans;
}
//一年前写的
/*#include <stdio.h>
long long quickpower(long long a, long long b, long long p);
int main()
{
long long a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
long long res = quickpower(a, b, p) % p;
printf("%lld^%lld mod %lld=%lld", a, b, p, res);
return 0;
}
//快速幂:一个数取余并且不断对2除,逆序结果就是2进制码
long long quickpower(long long a, long long b, long long p)//a的b次幂 a15=(a7)*(a7)*a a7=(a3)*(a3)*a a3=(a1)*(a1)*a1
{
long long x = a, ans = 1;//递归
while (b > 0) {
if (b % 2 == 1)
ans = ans * x % p;
x = x * x % p;
b /= 2;
}
return ans % p;
}*/