算法 {二进制位运算}

算法 {二进制位运算}

@LOC_0

二进制位运算

定义

{取反~(单目), 取负-(单目), &, |, ^(双目), >>, <<(双目)}

− x -x x取负运算, 是完全只关注机器码的, 不会关注这个数的符号 或者用的什么编码; 也就是, 最高符号位(如果是有符号数) 与其他位 是同等对待的;
. 对于整数 a a a, − a -a a(取负号) 就等价于 对 a a a机器码进行补码操作(即取反加一);
. 比如a= [0000 0011], -a = [1111 1101];

~, &, |, ^ 这几个运算, 是完全只关注机器码的, 不会关注这个数的符号 或者用的什么编码; 也就是, 最高符号位(如果是有符号数) 与其他位 是同等对待的;
. 比如, (负数) | (非负数) 他的结果 一定是负数 (因为最高位是1 | 0);

<<, >> 他分算术移位, 逻辑移位(全补0);
. c++使用的是算术移位, 即x (<<, >>) ?: 如果x是无符号的 则左右都补0, 否则x是有符号的 则右侧(即低位)补0, 而左侧(高位) 补符号位 (即负数 高位补1, 否则补0);

性质

int a = 1<<31, 虽然说 a是有符号的, 但他的比特位 还是10...0, 只不过 他的数值 是负数;
. 即二进制 他非常底层 与有无符号类型无关, 即int/uint x = 1<<31 他的比特位一定是10...0;

@DELI;

a , − a a, -a a,a的符号 可能是相同的;

a = M I N a = MIN a=MIN时, 此时进行补码操作后, 他的机器码 是不变的, 也就是 a = − a a = -a a=a;

还有个情况, 即 a = 0 a = 0 a=0时, 此时 也存在 a = − a a = -a a=a (即进行补码操作后, 两者的机器码是一样的);

@DELI;

对于一个二进制数, 如果存在两个1 他俩的距离为k, 则称该数是非法的;
. 比如 k = 1 k=1 k=1, 也就是不可以有 两个1 是相邻的;

可以通过if( (st << k) & st){ 非法};
. 证明: 假如有两个1 位置是a < bb - a = k (这个数是非法的), 那么a通过左移 k k k位以后, 他就到b位置的, 此时两个1 都在b位置, 就冲突了;
. 这是O(1)的 (你使用右移 即(st >> k) & st也可以); 如果你遍历循环这个数 则是 O ( N ) O(N) O(N)的;

@DELI;

向下取整;

a >> 1操作, 根据补码原理, 得到: (不管a是正数负数, 结果都是: 向下取整)

这与a % 2是不同的, 除法运算是向0取整; (比如: 5%2 -> 2; -5%2 -> -2)
a >> 1会得到: (5>>1 -> 2; -5>>1 -> -3);

@DELI;

*有符号整数x, 设其机器码为Bit[0,1,2,..., N-1(MSB最高符号位)], 若存在`ind`使得`Bit[ind] = 1`且`ind != MSB` (若有多个满足此的ind 则取最小的ind)*;
则`x & -x`的值 等于 `1 << ind`;

@DELI;

*有符号整数x, 设其机器码为Bit[0,1,2,..., N-1(MSB最高符号位)], 若存在`ind`使得`Bit[ind] = 0`且`ind != MSB` (若有多个满足此的ind 则取最小的ind)*;
则`(~x) & -(~x)`的值 等于 `1 << ind`;

算法

获取[0...R]bitsCount之和| 获取[0...R]里二进制位[bit]==1的数的个数

//{
template< class _T_> _T_ ___Binary_CountOfBit( _T_ _rig, int _bit){ // [0,...,_rig]里 `二进制[bit]==1`的个数(比如`rig=5, bit=1` 答案为`2: 0b10,0b11`;
ASSERTsystem_( (std::is_same_v<_T_,unsigned int> || std::is_same_v<_T_,unsigned long long> || std::is_same_v<_T_,unsigned __int128>));
constexpr int BITS = sizeof(_T_)*8;
    ASSERTsystem_( _bit>=0 && _bit<BITS, _bit, BITS);
    if( _rig < (_T_(1) << _bit)){
        return 0;
    }
    //>< `&rig = [10 ? 01], &bit = 2`
    _T_ higCount = (_rig >> (_bit+1)); // `0b10`
    _T_ lowCount = (_rig & ((_T_(1)<< _bit)-1)); // `0b01`
    _T_ ANS = (_rig + 1);
    ASSERTsystem_( _rig != _T_(-1)); // 否则`rig+1`就溢出了;
    ANS -= (higCount * (_T_(1) << _bit)); // [(00/01) 0 (...)]
    if( (_rig>> _bit) & 1){ // [10 0 (...)]
        ANS -= (_T_(1) << _bit);
    }
    else{ // [10 0 (00|01)]
        ANS -= (lowCount + 1);
    }
    return ANS;
}
template< class _T_> _T_ ___Binary_BitCountSum( _T_ _rig){ // [0,...,_rig]里 每个数的`bitCount`之和 (比如`rig=3` 答案为`0+1+1+2`);
ASSERTsystem_( (std::is_same_v<_T_,unsigned int> || std::is_same_v<_T_,unsigned long long> || std::is_same_v<_T_,unsigned __int128>));
constexpr int BITS = sizeof(_T_)*8;
    _T_ ANS = 0; // @WARN: 用`_T_`可能会溢出;
    for( int i = 0; i < BITS; ++i){ ANS += ___Binary_CountOfBit( _rig, i);}
    return ANS;
}

直接计算[bit]==1的个数 不好算, 我们计算 [bit]==0的个数;
分为两种: {(小的) 0 (任意), 和 (最大) 0 (?)}}, 第二种情况 此时这个? 也可能是任意 也可能是小的, 这取决于rig[bit]的值, 如果是1?=任意, 否则 比如rig = ...0 x 那么此时? = [0,1,2,...,x];

遍历所有的1

for( auto t = mask; t != 0;){
    auto bit = __builtin_ctz( t);  t ^= (1<<bit);
	//>< `mask[bit] == 1`;
}

@DELI;

这种方式, 效率比直接暴力(即FOR(bit,0,31){ if( (mask>>bit)&1) }) 快1倍;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值