位运算复习
前言
首先向 学长Luckblock谢罪自己位运算没学好/kk
话说考场推错位运算的公式就……关键自己试了不少样例居然都过了……
现在回顾学长的讲课内容
文章内容选自
学长博客
MosumLucifer博客
比特B(bit)
1GB = 1024MB = 1024^2 KB = 1024^3 Bit
Bit 指二进制中的一个 2 进制位,是信息的最小单位。
只包含 0/1 两种状态。
对应着内存条中的通电/断电。
关于变量
变量是内存中开辟的,一段用于储存数据的空间。
一个 int,32 个比特,32 个 2 进制位。
一个 char,8 个比特,8 个 2 进制位。
将一个变量内所有比特存储的数顺次排列,看成一个2进制数。即为变量存储的值。
负数是通过补码来完成的取反后+1:一个变量的第一个 2 进制位设为符号位,第一个 2 进制位为 0 表示正数,为 1 则表示负数。这样的表示法称为补码。
ull
扔去最高的符号位 不能表示负数
位运算
直接对补码进行操作
位运算符号
add:
int add(int x,int y){
int sum = (x^y);
int carry = (x&y)<<1;
while(carry != 0){
int a = sum,b = carry;
sum = (a ^ b);
carry = (a&b) <<1;
}
return sum;
}
清晰的写法(好吧上面的写法是由下面的推出来的):
int add(int x,int y){
return ((x&y)<<1)+(x^y);
}
加法与减法同理,减去一个正数等于加上一个负数,负数二进制位补码(取反+1)那么就可以直接加它的相反数
subtrack
int subtrack(int x,int y){
int subtractor = add(~y,1);
return add(x,subtractor);
}
multiply
- 将乘法转换成加法:效率低下显然的做法略过
- 更正算法
(1) 判断乘数是否为0,为0跳转至步骤(4)
(2) 将乘数与1作与运算,确定末尾位为1还是为0,如果为1,则相加数为当前被乘数;如果为0,则相加数为0;将相加数加到最终结果中;
(3) 被乘数左移一位,乘数右移一位;回到步骤(1)
(4) 确定符号位,输出结果;
int multiply (int x, int y){
int multiplicand = x < 0 ? add(~x,1) : x;
int multiplier = y < 0 ? add(~y,1) : y;
int product = 0;
while(multiplier > 0) {
if((multiplier & 1) > 0) {
protuct = add(protcut ,multiplicand);
}
multiplicand <<= 1;
multiplier >>= 1;
}
if((a ^b) < 0){
product = add(~product,1);
}
return product ;
}
4. 除法运算
除法运算很容易想到可以转换成减法运算,即不停的用除数去减被除数,直到被除数小于除数时,此时所减的次数就是我们需要的商,而此时的被除数就是余数。这里需要注意的是符号的确定,商的符号和乘法运算中乘积的符号确定一样,即取决于除数和被除数,同号为证,异号为负;余数的符号和被除数一样。同样的效率低下
由于每一个数都可以又\(2^i\)来表示出来那就可以用\(2^i\)来逐步尝试减去被除数,相应的倍数加入商中,减不动就用更下的\(i\)
如此可以快速求得答案
int divide_v2(int a,int b) {
int dividend = a > 0 ? a : add(~a, 1);
int divisor = b > 0 ? a : add(~b, 1);
int quotient = 0;
int remainder = 0;
for(int i = 31; i >= 0; i--) {
if((dividend >> i) >= divisor) {
quotient = add(quotient, 1 << i);
dividend = substract(dividend, divisor << i);
}
}
if((a ^ b) < 0){
quotient = add(~quotient, 1);
}
remainder = b > 0 ? dividend : add(~dividend, 1);
return quotient;
}
注意位运算尽可能加括号……运算优先级太低了
快速幂(由每个数可以拆成\(2^i\)的加和得到)
ll quicklypow(ll x, ll y ,ll mod){
ll ans = 1;
for(;y;y >> =1) {
if(y&1) ans = ans * x % mod;
x = x * x % mod;
}
return ans;
}
大整数带模乘法(大魔法)
由乘法分配率知\(x(a+b) = x * a + x * b\)
将y拆成二进制的数,类比快速幂
ll quickly(ll x, ll y, ll mod){
ll ans = 0;
for(; y; y >>=1) {
if(y&1) ans = (ans + x) % mod;
x = (x * x ) % mod;
}
return ans;
}
状压:
把 int 当长度为 32 的 bool 数组用。
右侧数第 1 个二进制位位称为第 0 位。
后记
对位运算处理的理解加深,计算机对位运算的使用理解透了,一些用到位运算的公式题也就会推导了
ps:有错误希望指正啊