位运算及其应用

文章介绍了位运算符,如与(&),或(|),异或(^),左移(<<),右移(>>)以及它们在判断数字奇偶性、计算二进制中1的个数、检测是否为2的幂次方等方面的应用。此外,还阐述了如何使用二进制枚举算法来生成集合的全部子集。
  1. 运算符号

& 与运算
|  或运算
^ 异或运算
<< 左移运算
>> 右移运算
~ 取反运算
  1. 利用与1按位与运算判断奇偶

因为偶数变为2进制后最后一位为0,奇数变为2进制后最后一位为1;而1变为2进制后,除了最后一位其余位都为0;所以,偶数与1按位与后为0,奇数与1按位与后为1。
print(6 & 1) # 0
print(4 & 1) # 0
print(8 & 1) # 0
print(3 & 1) # 1
print(1 & 1) # 1
print(5 & 1) # 1
  1. 计算二进制中1的个数

已知 X&(X-1)可以将最右边的1变为0,那么我们可以一直进行此步骤,直到X为0时,执行次数即为1的个数。
# 验证x&(x-1)的功能
x = 7
print(x & (x - 1))  # 6
x = 4
print(x & (x - 1))  # 0


# 统计二进制串中1的个数

def collect(x):
    count = 0
    while x != 0:
        x = x & (x - 1)
        count += 1
    return count


print(collect(4))  # 1
print(collect(7))  # 3
  1. 判断某数是否为2的幂次方

判断X&(X-1)是否为0.因为2的幂次方对应的2进制只有一个1,比如4,8,16。
def pan(x):
    if x & (x - 1) == 0:
        print("%s是2的幂次方" % x)
    else:
        print("%s不是2的幂次方" % x)


pan(4)  # 4是2的幂次方
pan(5)  # 5不是2的幂次方
  1. 二进制枚举子集

先来介绍一下「子集」的概念。

  • 子集:如果集合 A 的任意一个元素都是集合 S 的元素,则称集合 A 是集合 S 的子集。可以记为 A∈S

有时候我们会遇到这样的问题:给定一个集合 S,枚举其所有可能的子集。

枚举子集的方法有很多,这里介绍一种简单有效的枚举方法:「二进制枚举子集算法」。

对于一个元素个数为 n 的集合 S 来说,每一个位置上的元素都有选取和未选取两种状态。我们可以用数字 1 来表示选取该元素,用数字 0 来表示不选取该元素。

那么我们就可以用一个长度为 n 的二进制数来表示集合 S 或者表示 S 的子集。其中二进制的每一个二进位都对应了集合中某一个元素的选取状态。对于集合中第 i 个元素来说,二进制对应位置上的 1 代表该元素被选取,0 代表该元素未被选取。

举个例子,比如长度为 5 的集合 S={5,4,3,2,1},我们可以用一个长度为 5 的二进制数来表示该集合。

比如二进制数 11111(2) 就表示选取集合的第 1 位、第 2 位、第 3 位、第 4 位、第 5 位元素,也就是集合 {5,4,3,2,1},即集合 S 本身。如下表所示:

集合 S 中元素位置

5

4

3

2

1

二进位对应值

1

1

1

1

1

对应选取状态

选取

选取

选取

选取

选取

再比如二进制数 10101(2) 就表示选取集合的第 1 位、第 3 位、第 5 位元素,也就是集合 {5,3,1}。如下表所示:

集合 S 中元素位置

5

4

3

2

1

二进位对应值

1

0

1

0

1

对应选取状态

选取

未选取

选取

未选取

选取

再比如二进制数 01001(2) 就表示选取集合的第 1 位、第 4 位元素,也就是集合 {4,1}。如下标所示:

集合 S 中元素位置

5

4

3

2

1

二进位对应值

0

1

0

0

1

对应选取状态

未选取

选取

未选取

未选取

选取

通过上面的例子我们可以得到启发:对于长度为 5 的集合 S 来说,我们只需要从 00000∼11111 枚举一次(对应十进制为 0∼25−1)即可得到长度为 5 的集合 S 的所有子集。

我们将上面的例子拓展到长度为 n 的集合 S。可以总结为:

  • 对于长度为 n 的集合 S 来说,只需要枚举 0∼2n−1(共 2n 种情况),即可得到集合 S 的所有子集。

准备知识:x>>j&1 判断右数第j+1位(从1开始计数)是否为1
def sub(arr):
    n = len(arr)
    sub_list = [] 
    for i in range(1 << n): # 从0到2^n-1
        sub = []
        for j in range(n):
            if i >> j & 1:
                sub.append(arr[j])
        sub_list.append(sub[:])
    return sub_list


print(sub([1, 2]))  # [[], [1], [2], [1, 2]]
print(sub([]))  # [[]]
print(sub([1, 2, 3]))  # [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值