快速幂取模算法学习:
运用地方:
求解a^b%c的操作,如果a,b都不大当然可以直接进行操作,但是如果a,b都比较大的时候,首先幂次起来有可能会变成大数,操作起来不方便,对于每次计算都是一个O(n)的复杂度,进行很多次这样的求解的话复杂度就会太高。所以需要用到更快捷的方法。
前提知识->模运算的基本性质:
如果a%n=c%n且b%n=d%n,那么就有
1. (a-b)%n = (c-d)%n
2. (a+b)%n = (c+d)%n
3. (a*b)%n = (c*d)%n
利用这些基本性质:
因为a%n = a%n%n,b%n = b%n%n
所以可得 (a*b)%n = ((a%n)*(b%n))%n
算法内容:
要求
xn
,直接做的话需要n次,假设n=
2k
,那么我们就可以用k次
x2
相乘求出
xn
。试着将n 表示成2的幂次的和,n = 2^(k1)+2^(k2)+…,
xn
也就为x^(2^(k1)) * x^(2^(k2)) * …从而简化了计算步骤。
又可以发现n的表示法跟它的二进制表示有关系,将n表示成2进制,如果第k位为1那么n的组成有一部分为2^k。(最右边的一位为第0位)。这样就可以将x的这一部分乘起来了。
举个例子:x^23 = x^16 * x^4 * x^2 * x^1;23->10111(二进制表示)。
n = 2^4 + 2^2 + 2^1 + 2^0
例题:uva10006
代码:
mod_pow 函数中为快速幂取模的主要代码。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 65001
typedef long long ll;
bool is_prime[M];
int n;
void prime()
{
fill(is_prime,is_prime+M,true);
for(int i = 2;i < M;i++)
{
if(is_prime[i])
{
for(int j = 2*i;j < M;j += i)
{
is_prime[j] = false;
}
}
}
}
ll mod_pow(ll x,ll mod,ll n)
{
ll ans = 1;
while(n>0)
{
if(n & 1) ans = ans*x%mod;
x = x*x%mod;
n >>= 1;
}
return ans;
}
int main()
{
prime();
while(scanf("%d",&n)==1 && n)
{
bool ok = 0;
if(is_prime[n])
printf("%d is normal.\n",n);
else
{
for(int i = 2;i < n;i++)
{
ll temp = mod_pow((ll)i,(ll)n,(ll)n);
if(temp!=i)
{
ok = 1;
break;
}
}
if(ok) printf("%d is normal.\n",n);
else
printf("The number %d is a Carmichael number.\n",n);
}
}
return 0;
}