位运算复习

前言

首先向 学长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. 将乘法转换成加法:效率低下显然的做法略过
  2. 更正算法
    (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:有错误希望指正啊

posted @ 2020-11-03 16:45  Imy_bisLy  阅读(187)  评论(3编辑  收藏  举报