1. 程式人生 > >快速冪及其取餘

快速冪及其取餘

快速冪

快速冪的作用:快速計算底數的n次冪。
時間複雜度為:O(log₂N) (樸素方法為O(N)

原理

以下以求a的b次方來介紹
把b轉換成二進位制數
該二進位制數第i位的權為2i1
例:
  a11=a20+21+23
11的二進位制是1011
11=23×1+22×0+21×1+20×1
因此,我們將a11轉化為算a20×a21×a23

程式碼

遞迴版

int powf(int a, int b){
    if(b == 1)
        return
a; int t = powf(a, b/2); return (b%2 == 0 ? 1 : a)*t*t; }

迴圈版

int pow0(int a, int b){
    int r = 1, base = a;
    while(b != 0){
        if(b%2)
            r *= base;
        base *= base;
        b /= 2;
    }
    return r;
}
//常規求冪
int pow1(int a, int b){
    int r = 1;
    while(b--)
        r *= a;
    return
r; } //快速求冪(位運算) int pow2(int a, int b){ if(b == 0) return 1; else { while((b&1) == 0){ b >>= 1; a *= a; } } int r = a; b >>= 1; while(b != 0){ a *= a; if(b & 1) r *= a; b >>= 1
; } return r; } //快速求冪(位運算,簡潔版) int pow3(int a, int b){ int r = 1, base = a; while(b){ if(b & 1) r *= base; base *= base; b >>= 1; } return r; }

測試

#include <stdio.h>
//a^b
int powf(int a, int b){
    if(b == 1)
        return a;
    int t = powf(a, b/2);
    return (b%2 == 0 ? 1 : a)*t*t;
}
int pow0(int a, int b){
    int r = 1, base = a;
    while(b != 0){
        if(b%2)
        r *= base;
        base *= base;
        b /= 2;
    }
    return r;
}
//常規求冪
int pow1(int a, int b){
    int r = 1;
    while(b--)
        r *= a;
    return r;
}
//快速求冪(位運算)
int pow2(int a, int b){
    if(b == 0)
        return 1;
    else {
        while((b&1) == 0){
            b >>= 1;
            a *= a;
        }
    }
    int r = a;
    b >>= 1;
    while(b != 0){
        a *= a;
        if(b & 1)
            r *= a;
        b >>= 1;
    }
    return r;
}
//快速求冪(位運算,簡潔版)
int pow3(int a, int b){
    int r = 1, base = a;
    while(b){
        if(b & 1)
            r *= base;
        base *= base;
        b >>= 1;
    }
    return r;
}

int main(void)
{
    int a, b;
    scanf("%d%d", &a, &b);
    printf("遞迴版:\n");
    printf("%d^%d = %d\n\n", a, b, powf(a, b));
    printf("迴圈版:\n");
    printf("%d^%d = %d\n\n", a, b, pow0(a, b));
    printf("常規求冪:\n");
    printf("%d^%d = %d\n\n", a, b, pow1(a, b));
    printf("位運算1:\n");
    printf("%d^%d = %d\n\n", a, b, pow2(a, b));
    printf("位運算2:\n");
    printf("%d^%d = %d\n\n", a, b, pow3(a, b));
    return 0;
}

快速冪取餘

首先要證明一個公式:a*b%m = [(a%m)(b%m)]%m
a=a1m+a2
b=b1m+b2
a*b%m = a2b2%m = [(a%m)(b%m)]%m
進而
ab%m = [(a%m)b]%m = [(a%m)(a%m)b1]%m = { (a%m)[(a%m)b1%m] }%m

程式碼

#include <stdio.h>
long long powMod(long long a, long long b, long long mod)
{
    long long ans = 1;
    a %= mod;
    while(b){
        if(b&1)
            ans = ans*a%mod;
        a = a*a%mod;
        b >>= 1;
    }
    return ans;
}
int main(void)
{
    int a, b, mod;
    scanf("%d%d%d", &a, &b, &mod);
    printf("%d^%d%%%d = %lld\n\n", a, b, mod, powMod(a, b, mod));
    return 0;
}