---
# System prepended metadata

title: 題解：快速冪(上)
tags: [solution]

---

---
title : 題解：快速冪(上)
tags : solution
---

# 快速冪計算流程

稍微展示一下，快速冪的運作流程：`ex` $7^{26}$


| 次方數二進位 | 當前位元狀態 | 當前倍數 | 計算後答案 |
| - | - | - | - |
| <font color=blue>11010</font> |  |  | $1$ |
| <font color=blue>1101</font><font color=red>0</font> | <font color=red>0</font> | $7^1$ | $1$ |
| <font color=blue>110</font><font color=red>1</font><font color=gray>0</font> | <font color=red>1</font> | $7^2$ | $1×$<font color=red>$7^2$</font> |
| <font color=blue>11</font><font color=red>0</font><font color=gray>10</font> | <font color=red>0</font> | $7^4$ | $1×$$7^2$ |
| <font color=blue>1</font><font color=red>1</font><font color=gray>010</font> | <font color=red>1</font> | $7^8$ | $1×$$7^2×$<font color=red>$7^8$</font> |
| <font color=red>1</font><font color=gray>1010</font> | <font color=red>1</font> | $7^{16}$ | $1×$$7^2×$$7^8×$<font color=red>$7^{16}$</font> |
| <font color=gray>11010</font> |  |  | $1×$$7^2×$$7^8×$$7^{16}=7^{26}$ |

快速冪有個「>>=」的處理，這就是在跑「移動紅色位置」的部分，
(作用在於判斷最右邊位元數( if n&1 )後，將整個二進位都往右推一位)
而 x*=x 的處理，用途在於將「次方數值往左推一位」，
亦可以看成是：( $7^1,7^2,7^4,7^8,7^{16}$ ) $·$ ( $0,1,0,1,1$ )，把內積運算過程的加號改成乘號之計算結果
(原本的二進位是 ( $1,2,4,8,16$ ) $·$ ( $0,1,0,1,1$ ) 的內積運算結果)

---

# 從二進位到快速冪(程式碼比較)

計算二進位為1的數量：
```cpp=
int bit_amount(int n){
    int r = 0;
    
    while(n){
        if(n&1)
            r++;
        n>>=1;
    }
    
    return r;
}
```
次方快速冪，多了需要處理倍數的部分：
```cpp=
int pow(int x, int n){
    int r = 1;
    
    while(n){
        if(n&1)
            r*=x;
        x*=x;
        n>>=1;
    }
    
    return r;
}
```
次方快速冪，多了處理模運算的部分：
```cpp=
int pow(int x, int n, int MOD){
    int r = 1;
    
    while(n){
        if(n&1){
            r*=x;
            r%=MOD
        }
        x*=x;
        x%=MOD
        n>>=1;
    }
    
    return r;
}
```
---

# ZJ d636 - 大爆炸
- [d636 - 大爆炸](https://zerojudge.tw/ShowProblem?problemid=d636)

典型的次方快速冪題目。

```cpp=
# include <stdio.h>

#define MOD 10007

int main(){

    int a,n ,r = 1;
    scanf("%d%d",&a,&n);
    do{
        if(n&1){
            r*=a;
            r%=MOD;
        }
        a*=a;
        a%=MOD;
    }while(n>>=1);

    printf("%d",r);
}
```

>執行結果：
> Time : 1 ms
> Memory : 84 KB

---
# UVa 374 - Big Mod
- [374 - Big Mod](https://onlinejudge.org/index.php?option=onlinejudge&Itemid=8&page=show_problem&problem=310)

與上一題幾乎相同，只不過型態要記得開大一點
(如果把 $2^{31}-1$ 平方，就會超出int值域，使答案溢位(overflow) )。

```cpp=
# include <iostream>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    long long int a,n,mod,r;
    while(cin>>a>>n>>mod){
        r = 1;
        while(n){
            if(n&1){
                r*=a;
                r%=mod;
            }
            a*=a;
            a%=mod;
            n>>=1LL;
        }
        cout<<r<<'\n';
    }
}

```
>執行結果：
> Time : 0 ms

---
# POJ 1995 - Raising Modulo Numbers
- [1995 - Raising Modulo Numbers](http://poj.org/problem?id=1995)

通常會把快速冪寫成函式(pow)，最主要原因還是因為邏輯方便(#

```cpp=
# include <stdio.h>

typedef long long ll;

ll pow(ll a, ll b, const int &M){
    ll r = 1;

    do{
        if(b&1){
            r*=a;
            r%=M;
        }
        a*=a;
        a%=M;
    }while(b>>=1);
    
    return r;
}

int main(){
    int Z,M,H;
    ll a,b,sum;
    
    scanf("%d",&Z);
    while(Z--){
        scanf("%d%d",&M,&H);
        sum = 0;
        
        while(H--){
            scanf("%lld%lld",&a,&b);
            sum += pow(a,b,M);
            sum %= M;
        }
        
        printf("%lld\n",sum);
    }
}

```

> 執行結果：
> Time : 125 ms
> Memory : 88 KB


---
# LeetCode 50 - Pow(x,n)
- [50 - Pow(x,n)](https://leetcode.com/problems/powx-n/)

僅只是把整數底換成小數底，並且要考慮負數次方的快速冪問題
(甚至還不需要考慮模算數)
(註：矩陣快速冪，就是把底換成「矩陣」，以此類推)

```cpp=
double myPow(double x, long long n) {
    double r = 1;
    if(n<0)
        x = 1/x, n = -n;

    do{
        if(n&1)
            r*=x;
        x*=x;
    }while(n>>=1);

    return r;
}
```

> 執行結果：
> Time : 0 ms (Rated 100%)
> Memory : 5.9 MB (Rated 86.08%)


###### posted date: `2021.3.7` 