---
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`