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)
本文由“小苏打NaHCO3”原创,未经允许,严禁转载!
如果觉得本文对你有用,不妨点赞收藏一下吧!