0x01 位运算(1)
0x01 位运算(1)
本节主要内容:
第一部分:补码反码、移位运算、快速幂、快速乘法
第二部分:二进制状压、成对变换、lowbit()运算,Barrett模乘(补充)
一、 补码与反码
1. 补码
开头一个符号位,其余为数值位
| 32位补码表示 | unsigned int | int |
|---|---|---|
| 000000...000000 | 0 | 0 |
| 0111111...111111 | 2147483647 | 2147483647 |
| 100000...000000 | 2147483678 | -2147483648 |
| 111111...111111 | 4294967295 | -1 |
1. 反码
开头一个符号位,其余为数值位
| 32位反码表示 | 32位整数 |
|---|---|
| 000000...000000 | 0 |
| 111111...111111 | 0 |
| 011111...111111 | 2147483647 |
| 100000...000000 | -2147483647 |
二、 移位运算
-
算术右移:高位以符号位补充,低位越界舍弃
-
逻辑右移:高位以0补充,低位越界舍弃
三、 快速幂取模
此时尝试计算
\[a^bmod\\quad p
\]
这是一个典型的求幂运算,在一些与数学有关的问题中极为常见。
通常使用循环方法求幂,时间复杂度为O(b),然而众所周知,取模运算的速度极慢,这种暴力方法不足以满足我们的需求,我们需要尽可能减少运算次数。
接下来介绍利用位运算加速的求幂方法,先给出理论推导
假设指数b在二进制下表示为
\[b\_{(2)} = \\overline{c\_{k-1}c\_{k-2} \\dots c\_0}
\]
写为多项式,即为
\[b = c\_{k-1} \\cdot 2^{k-1} + c\_{k-2} \\cdot 2^{k-2} + \\dots + c\_0\\cdot2^0
\]
从而
\[\\begin{aligned} a^b &= a^{c\_{k-1}\\cdot{2^{k-1}}} \* a^{c\_{k-2}\\cdot{2^{k-1}}}\*\\dots\*a^{c\_0\*2^0}\\\\ &=(a^{c\_{k-1}\\cdot{2^{k-2}}} \* a^{c\_{k-2}\\cdot{2^{k-3}}}\*\\dots\*a^{c\_1\*2^0})^2\*a^{c\_0\\cdot2^0}\\\\ &=((a^{c\_{k-1}\\cdot{2^{k-3}}} \* a^{c\_{k-2}\\cdot{2^{k-4}}}\*\\dots\*a^{{c\_2}\*2^0})^2\*a^{c\_1\*2^0})^2\*a^{c\_0\\cdot2^0}\\\\ &=\\dots\\\\ &=(((a^{c\_{k-1}\\cdot2^0})^2\*a^{c\_{k-2}\\cdot2^0})^2\*\\dots)^2\*a^{c\_0\\cdot2^0}\\\\ &=(((a^{c\_{k-1}})^2\*a^{c\_{k-2}})^2\*\\dots)^2\*a^{c\_0} \\end{aligned}
\]
于是,可以用下面的代码求出a^b:
int power(int a,int b){ //calculate a^b
int ans = 1;
while(b){
if (b & 1) ans *= a;
a *= a;
b >>= 1;
}
return ans;
}
同时,考虑到
\[a\\cdot b \\quad \\%p == a\\%p\\quad\\cdot\\quad b\\%p
\]
则在循环中每次计算取模
int power(int a,int b, int p){ //calculate a^b
int ans = 1 % p;
while(b){
if (b&1) ans = (ll)ans * a % p;//暂时转化为ll,以免越界,赋值时再转化回int
a = (ll)a * a % p;
b >>= 1;
}
return ans;
}
从而,我们计算出a^b mod p,时间复杂度为O(log2b)
四、 64位整数乘法
题目链接:64位整数乘法——ACWing
类似于快速幂取模,我们用相似的方法处理乘法。
此时尝试计算
\[ab\\quad mod\\quad p
\]
假设b在二进制下表示为
\[b = \\overline{c\_{k-1}c\_{k-1}\\dots c\_0}
\]
写为多项式,即为
\[b = c\_{k-1} \\cdot 2^{k-1} + c\_{k-2} \\cdot 2^{k-2} + \\dots + c\_0 \\cdot 2^0
\]
从而
\[\\begin{aligned} ab &= ac\_{k-1}\\cdot 2^{k-1}+ac\_{k-2}\\cdot2^{k-2}+\\dots+ac\_0\\cdot2^0\\\\ &= (ac\_{k-1}\\cdot 2^{k-2} + ac\_{k-2}\\cdot2^{k-3}+\\dots+ac\_1\\cdot2^0)\*2+ac\_0\\cdot2^0\\\\ &= ((ac\_{k-1}\\cdot 2^{k-3} + ac\_{k-2}\\cdot2^{k-4}+\\dots+ac\_2\\cdot2^0)\*2+ac\_1\\cdot2^0)\*2+ac\_0\\cdot2^0\\\\ &=\\dots\\\\ &=(((ac\_{k-1}\\cdot2^0)\*2+ac\_{k-2}\\cdot2^0)\*2+\\dots)\*2+ac\_0\\cdot2^0\\\\ &=((ac\_{k-1}\*2+ac\_{k-2})\*2+\\dots)\*2+ac\_0\\\\ &=\[((c\_{k-1}\*2+c\_{k-2})\*2+\\dots)\*2+c\_0\]\\cdot a\\\\ \\end{aligned}
\]
于是,可以用下面的代码求出ab
ll mul(ll a, ll b){
ll ans = 0;
while(b){
if (b & 1) ans += a;
a *= 2;
b >>= 1;
}
return ans;
}
考虑取模:
ll mul(ll a, ll b){
ll ans = 0;
while(b){
if (b & 1) ans = (ans + a) % p;
a = a * 2 % p;
b >>= 1;
}
return ans;
}
从而,我们计算出ab mod p,时间复杂度为O(log2b)
本文由“庞加莱复现”原创,未经允许,严禁转载!
如果觉得本文对你有用,不妨点赞收藏一下吧!

浙公网安备 33010602011771号