这几天做题的时候突然想稍微研究下这个。总结下快速幂的想法。
算理分析:
快速幂,顾名思义,是快速求解高次幂的算法。
其实这是用在密码学中的快速幂取模算法。
比如:
由取模定义:
56*89mod(a)=56mod(a)*89mod(a)。
可知:
56^56mod(1526)=56^55*56mod(1526)
=56^54mod(1526)*56^2mod(1526)
=56*56*56***56(56个)mod(1526)
这样就将一个非常大的数字的mod运算变成最多循环56次的mod运算。
算法:
typedef long long ll;
int quick_mod(int x, int n, int mod){
ll res = 1;
while (n > 0){
if (n & 1)//与1与运算,判断是否为奇数
res = res*x%mod;
x = x*x%mod;
n >>= 1;//二进制右移一位,相当于n/2,前面已经保证奇数时的运算了。
}
return res;
}
算法应用:
题目:
求x^n的最后3位数。
题目链接:HDU 2035 人见人爱A^B
那么只要quick_mod(x,n,1000);
即可。
原理剖析:(相关性分析)
求运算的最后三位。这是一个循环的乘法运算。
任何一个乘法计算后的数字,其后三位只和相乘两个数的后三位相关。
且为直接相关。
高于三位的数字与计算后数字的后三位无相关性。
因此,只要保留每次运算的后三位即可,即为每次运算mod(1000).
题目升级1:
题目:
求x^n的前面3位数。
题目连接:(前一位数)
http://acm.hdu.edu.cn/showproblem.php?pid=1060
相关性分析:
这个就不能直接套用取模运算了。因为题目要求的是最前面的,不是最后面的。
在乘法运算中,前两位的相关性与运算数字的前三位强相关。
之后就是依次与最后一位程递减关系。
那么只要保证参与运算的前几位足够的多,得到的最前面3位就越精确。
下面是算法:
typedef long long ll;
int quick_power(ll x,ll n)
{
ll res = 1;
while (n > 0 ){
if (n & 1)
{
res = res*x;
if (res > 1000000000)
{//保证参与运算的数字有9位
while (res > 1000000000)
res = res / 10;
}
}
x = x*x;
if (x > 1000000000)
{//保证参与运算的数字有9位
while (x >1000000000 )
x = x / 10;
}
n >>= 1;
}
if (res >= 1000)
{//获得前三位
while (res >= 1000)
res = res / 10;
}
return res;
}
简单的用几个循环测试了下。
当幂在10000以内时,出错概率大概为0.20002%,误差极小,误差范围也是<0.0001,大概是格式取值的时候出现点问题。
当幂在100000以内时,出错概率大概为2.262%,误差范围<0.001。
当测试为为最前面1位时。
幂在10000以内时,出错概率接近0%。
当幂在100000以内时,0.02300023%。
数学技巧运用:(精确)
求n^n。
可以运用高数中对数的解题技巧。
x=n^n
loga(x)=n*loga(n)
设m=loga(x) ->x=a^m 即x=pow(a,m)
令a=10,即等式两边同时取10的对数log10。
x ^ x= 10^(x*lg(x))=10^(整数部分+小数部分) ;则x ^ x的最高位是由小数部分决定的(因为10的整数次幂不会影响最高位,只在最末位加0)。
应用上面的技巧,可以快速的求得。
算法:
#include <cstdio>
#include <iostream>
#include <map>
#include <algorithm>
#include <cmath>
#include <vector>
#include <functional>
#include <string>
using namespace std;
typedef long long ll;
int main()
{
ll m = 99999;
int T;
cin >> T;
int n, i, ans, num = 0, k;
double a, b;
//for (; m > 1; m = m - 1)
//{
while (T--){
scanf("%Id64", &m);
//k = getmod(m, m);
a = m*log10(m*1.0) - (long long)(m*log10(m*1.0));
b = pow((double)10, a);
//b = b * 100;
ans = (int)(b);
cout << ans << endl;
}
}
话说,我前面还那么麻烦干什么?0 0。