位运算_C++位运算基础

版权声明:本文为博主原创作品, 转载请注明出处! https://blog.csdn.net/solider98/article/details/83181818

1, 补码 = 反码 + 1 (求反码时保持符号位不变对余下所有位取反)

2, 补码表示如下: (以n位补码为例,约定:最低位为第0位)

    \overline{10...0}(含n - 1个0) : 表示最小的负整数: - 2^{^{n - 1}}

    \overline{11...1}(含n个1): 表示最大的负整数: - 1

    \overline{01...1}(含n - 1个1): 表示最大的正整数: 2^{n - 1} - 1

3, 将n为整数S(对应C++中使用补码表示的整型)所有位取反对应~S, 则~S对应的原码数值为-1 - S, -2^{n - 1} <= S <= 2^{n - 1} - 1

4, 任意的运算数类型均为(unsigned int), 且只含有+(加), -(减),*(乘)的C++算术表达式的最终结果均为将其实际计算结果对{\color{Red} 2^{32}}取模(注意:这里指数学意义上的取模运算, 不同于C++中的%运算, 稍后将进一步说明), 例如对于unsigned int a = 1e10, unsigned b = 1e10, 则C++计算a * b - (a + b) 的结果为一个unsigned int型数值,该数值等于 {\color{Red}(10^{10} * 10^{10} - (10^{10} + 10^{10})) mod 2^{32}}, 如果将结论中的unsigned int 替换为unsigned long long则C++的计算结果是对于{\color{Red} 2^{64}}取模,下面给出这一结论的证明:

      证明: 设s表示一个合法的,只含有+, -, *运算的, 所有操作数均为unsigned int(long, long long)类型的C++算术表达式, 定义s.len为s中所有运算符, 运算数和圆括号的总个数, 根据c++语法规则, 将一个整数(可以是负整数)p赋值给unsigned int型的变量a时, a被赋予的值为p mod 2^{32} ,因此当s.len为1时, 上述结论成立,

      假设当s.len <= k, (k >= 1, 且k是s.len可以取到的一个合法值)时上述结论成立, 设t为s.len可以取到的且大于k的最小值, 则此情况下, s必定形如 ({s}') 或 l op r, 前者指s的最左端和最右端是一对相匹配的括号, 也即s中所有的运算符均位于某个括号之内, 后者指s中存在不在任何括号内部的运算符. 对于前者C++编译器会先计算括号内部的{s}', 且{s}'.len <= k, 因此{s}'为其实际值对2^{32}取模, 对于后者C++编译器先计算子表达式l和r, 且l.len <= k, r.len <= k, 故l和r的计算结果均为其实际结果对2^{32}取模, 又有op必为+, -, *之一,根据模运算性质, C++将l op r的结果对2^{32}取模, 因此当s.len为t时上述结论成立, 故对任意s.len >= 1上述结论均正确,从而结论得证

5, C++ 常用数据类型及其范围和精度:

     int 范围: [-0x80 00 00 00, 0x7f ff ff ff], 即 []-2 147 483 648, 2 147 483 647] (10^{9}级别级别), 占用4个字节

     unsigned int 范围: [0x00 00 00 00, 0xff ff ff ff], 即 [0, 4 294 967 295] (10^{9}级别), 占用4个字节

     long long 范围: [-9 223 372 036 854 775 808, 9 223 372 036 854 775 807] (10^{18}级别), 占用8个字节

     unsigned long long 范围: [0, 18446744073709551615] (10^{19}级别), 占用8个字节

     double 有效数字(十进制表示下的最高位, 去尾近似): 15 ~ 16位

     long double 有效数字(十进制表示下的最高位, 去尾近似): 18 ~ 19位 

对于int变量, 常用0x3f3f3f3f表示+\infty, 因为该数是集合{n | n * 2 <= 2 147 483 647且n(int型32位))的每个字节中的值相同} 中的最大正整数

同时使用语句memset(arr, 0x3f, sizeof(arr)), 可将int数组arr中每个元素的值设置为0x3f3f3f3f

6, 对于任意整型(不局限于int)的变量a(可以为负整数), 满足a << i = a * 2^{i}, (i  >= 0, 且 a << i \neq0) 

      在将>>实现为算术右移(高位以符号位填充)的C++编译器中, 对于任意整型(不局限于int)的变量a(可以为负整数)有下述等式成立

      a >> i 等于 \left \lfloor a / 2^{i} \right \rfloor(注意:此处"/"是指数学上的除法计算, 而不是C++中的除法运算符)

    特别的, 2^{i} = 1 << i, a << 1 = a * 2, a >> 1 = \left \lfloor a/ 2 \right \rfloor(注意:此处"/"是指数学上的除法计算, 而不是C++中的除法运算符, 对于C++中的除法运算符计算两个整型变量a / b 的结果为将数学上a / b的结果向0取整, 同时对于C++中的%运算符满足: a % b 等于 a - (a / b) * b(此处的"/"作为C++中的除法运算符), 这也就时为什么C++中的%运算不同于数学上的模运算的原因.

7, 对于任意正整数m, 其在二进制表示下的位数为\left \lceil lg(m + 1) \right \rceil, (注意在计算机科学中lg表示以2为底的对数)

8, 设a\in \left \{ 0, 1 \right \}, 下面的&, |, ^ 为C++中的位与, 位或, 位异或运算符,则有:

    a & 0 = 0,    a & 1 = a         (适用于将某些位置0, 其它位不变)

    a | 0 = 1,    a | 1 = 1            (适用于将某些位置1, 其它位不变)

    a ^ 0 = a,    a ^ 1 = 1 - a      (适用于将某些位取反, 其它位不变)

9, 设n为非负整数, 定义lowbit(n)为n在二进制表示下由其"最低位的1及其后所有的0"构成的数值, 则有lowbit(n) = n & (-n), 证明从略.

在lowbit(n)定义的基础上可给出时间复杂度为二进制表示下n中1的个数线性函数的,用于计算a的二进制表示下1的个数的函数count(n)如下:

//返回非负整数n在其二进制表示下1的个数 
int count(int n){
	int ans = 0;
	while(n) n -= n & ( -n), ++ans;
	return ans;
}

10, 对于一定范围的非负整数k, 高效索引 lg2^{k}的技巧: \forall k\in [0, 35], 2^{k}mod37两两不等, 且取遍1 ~ 36, 示例如下:

int hash[37];
//将2^0...2^35索引到数组hash, 其中lg(2^k) =hash[a^k % 37] 
void hash(){
	for(int i = 0; i <= 35; ++i) hash[(1ll << i) % 37] = i;
}
//返回lgk的值, 要求k为2的整数次幂且k <= 2^35 
int getVal(long long k){
	return hash[k % 37];
}

11, 模运算性质

    (1), (a + b) mod p = (a mod p + b mod p) mod p

    (2), (a - b) mod p = (a mod p - b mod p) mod p

    (3), (a * b) mod p = ((a mod p) * (b mod p)) mod p

    (4), a^{b} mod p = (a\: mod\: p)^{b} mod p

猜你喜欢

转载自blog.csdn.net/solider98/article/details/83181818
今日推荐