快速幂取模 例题uva10006

快速幂取模算法学习:

运用地方:
求解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;
}

你可能感兴趣的:(快速幂取模 例题uva10006)