从集合论到位运算

2024-06-02 14:28
文章标签 运算 集合论 到位

本文主要是介绍从集合论到位运算,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言


本文将扫清位运算的迷雾,在集合论与位运算之间建立一座桥梁。

在高中,我们学了集合论(set theory)的相关知识。例如,包含若干整数的集合 S={0,2,3}。在编程中,通常用哈希表(hash table)表示集合。例如 Java 中的 HashSet,C++ 中的 std::unordered_set。

在集合论中,有交集 ∩、并集 ∪、包含于 ⊆等等概念。如果编程实现「求两个哈希表的交集」,需要一个一个地遍历哈希表中的元素。那么,有没有效率更高的做法呢?

该二进制登场了。

集合可以用二进制表示,二进制从低到高第 i 位为 1表示 i 在集合中,例如集合{0,2,3}可以用二进制1101表示;反过来,1101就对应集合{0,2,3}。正式地说,包含非负整数的集合S可以用以下方式压缩成一个数字:

                                                       f(S) = \sum_{i\in s}^{} 2^i

例如集合{0,2,3}可以压缩成2^0 + 2^2 + 2^3 = 13也就是二进制的1101。

利用位运算「并行计算」的特点,我们可以高效地做一些和集合有关的运算。按照常见的应用场景,可以分为以下四类:

  1. 集合与集合
  2. 集合与元素
  3. 遍历集合
  4. 枚举集合

一、集合与集合


其中 & 表示按位与,∣ 表示按位或,⊕ 表示按位异或,∼ 表示按位取反。

两个集合的「对称差」是只属于其中一个集合,而不属于另一个集合的元素组成的集合,也就是不在交集中的元素组成的集合。

术语集合位运算集合示例位运算示例
交集A∩Ba&b{0,2,3}∩{0,1,2} = {0,2}1101 & 0111 = 0101
并集A∪Ba ∣ b{0,2,3}∪{0,1,2} = {0,1,2,3}1101 | 0111 = 1111
对称差A Δ Ba⊕b{0,2,3}Δ{0,1,2} = {1,3}1101 ⊕ 0111 = 1010
A\Ba&∼b{0,2,3}\{1,2} = {0,3}1101 & 1001 = 1001

二、集合与元素

通常会用到移位运算。

其中 << 表示左移,>> 表示右移。

术语集合位运算集合示例位运算示例
空集0
单元素集合{i}1<<i{2}1<<2
全集U={0,1,2,⋯n−1}(1<<n)-1{0,1,2,3}(1<<4)-1
补集Cu​S=U\S((1<<n)-1)⊕sU={0,1,2,3}
Cu{1,2}={0,3}
1111 ⊕ 0110 = 1001
属于i \in S(s >> i) & 1=12 \in [{0,2,3}](1101 >> 2) & 1=1
不属于i \notin S(s >> i) & 1=01\notin [0,2,3](1101 >> 1) & 1=0
添加元素S∪{i}s | (1<<i){0,3}∪{2}1001 ∣ (1 << 2)
删除元素S \ {i}s&∼(1 << i){0,2,3}\{2}1101&∼(1 << 2)

特别地,如果 𝑠 是 2 的幂,那么 𝑠 &( 𝑠 − 1) =0。

此外,编程语言提供了一些和二进制有关的库函数,例如:

  • 计算二进制中的 1 的个数,也就是集合大小;
  • 计算二进制长度,减一后得到集合最大元素;
  • 计算二进制尾零个数,也就是集合最小元素。

三、遍历集合

设元素范围从0到n-1,挨个判断每个元素是否在集合s中

for (int i = 0; i < n; i++) {if ((s >> i) & 1) { // i 在 s 中// 处理 i 的逻辑}
}

四、枚举集合

枚举所有集合

设元素范围从 0 到 n−1,从空集 ∅ 枚举到全集 U:

for (int s = 0; s < (1 << n); s++) {// 处理 s 的逻辑
}

枚举非空子集

设集合为 𝑠,从大到小枚举 𝑠 的所有非空子集 sub:

for (int sub = s; sub; sub = (sub - 1) & s) {// 处理 sub 的逻辑
}

为什么要写成 sub = (sub - 1) & s 呢?

暴力做法是从s开始,不断减一,直到0但这样做,中途会遇到很多不是s的子集的情况。例如s=10101时,减1得到10100,这是s的子集,但是再减1就得到10011了。这并不是s的子集。下一个子集应该是10001。

把所有的合法子集按顺序列出来,会发现我们做的相当于「压缩版」的二进制减法,例如

                                10101→10100→10001→10000→00101→⋯

如果忽略掉 10101 中的两个 0,数字的变化和二进制减法是一样的,即

                                        111→110→101→100→011→⋯

如何快速跳到下一个子集呢?比如,怎么从 10100 跳到 10001?

  • 普通的二进制减法,是10100-1=10011,也就是把最低位的1变成0,对于最低位的1右边的0都变成1。
  • 压缩版的二进制减法也是类似的,对于10100->10001,也会把最低位的1变成0,对于最低位的1右边的0,并不是都变成1,只有在s=10101中的1才会变成1。怎么做到?减1后&10101就行,也就是(10100-1)&10101 = 10001

枚举子集(包含空集)

如果要从大到小枚举 𝑠 的所有子集 sub(从 𝑠 枚举到空集 ∅),可以这样写:

int sub = s;
do {// 处理 sub 的逻辑sub = (sub - 1) & s;
} while (sub != s);

原理是当sub=0时(空集),再减1就得到-1,对应的二进制位111...1,在&s就得到了s,所以循环到sub=s时,说明最后一次循环sub=0(空集),s的所有子集都枚举到了。退出循环。

这篇关于从集合论到位运算的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1024219

相关文章

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

uva 575 Skew Binary(位运算)

求第一个以(2^(k+1)-1)为进制的数。 数据不大,可以直接搞。 代码: #include <stdio.h>#include <string.h>const int maxn = 100 + 5;int main(){char num[maxn];while (scanf("%s", num) == 1){if (num[0] == '0')break;int len =

【Java中的位运算和逻辑运算详解及其区别】

Java中的位运算和逻辑运算详解及其区别 在 Java 编程中,位运算和逻辑运算是常见的两种操作类型。位运算用于操作整数的二进制位,而逻辑运算则是处理布尔值 (boolean) 的运算。本文将详细讲解这两种运算及其主要区别,并给出相应示例。 应用场景了解 位运算和逻辑运算的设计初衷源自计算机底层硬件和逻辑运算的需求,它们分别针对不同的处理对象和场景。以下是它们设计的初始目的简介:

位运算:带带孩子吧,孩子很强的!

快速进制 在聊到位运算之前,不妨先简单过一遍二进制的东西。熟悉二进制和十进制的快速转换确实是掌握位运算的基础,因为位运算直接在二进制位上进行操作。如果不熟悉二进制表示,很难直观理解位运算的效果。 这里主要涉及二进制和十进制之间的互相转换。 十进制转二进制 十进制转二进制可以使用常见的 除2取余法 进行。每次将十进制除以2并记录所得余数,直到商为0,然后再将记录的余数 从下往上排列即

C++ STL关联容器Set与集合论入门

1. 简介 Set(集合)属于关联式容器,也是STL中最实用的容器,关联式容器依据特定的排序准则,自动为其元素排序。Set集合的底层使用一颗红黑树,其属于一种非线性的数据结构,每一次插入数据都会自动进行排序,注意,不是需要排序时再排序,而是每一次插入数据的时候其都会自动进行排序。因此,Set中的元素总是顺序的。 Set的性质有:数据自动进行排序且数据唯一,是一种集合元素,允许进行数学上的集合相

快速幂运算的一些模板

这里用递归和循环两种做法来做。 简单来说,快速幂就是把底数扩大,指数缩小,比如2*2=4;计算2的幂时,就可以转换成4的幂来运算,这样可以避免在计算大的数据时爆int的现象  //递归int power(int a,int n){int ans;if(n==2) ans=1;else{ans=power(a*a,n/2);if(n%2==1) ans*=a;}return ans;}

高精度计算----减法运算(浮点型)

基于上一贴,修改减法运算适合于高精度浮点型计算。 因为减法比加法难度大一点,考虑的地方也要多一些,可能代码有欠缺,欢迎指出。 运算说明: 1、相减函数依旧没改变,包括上一贴的判断被减数与减数的大小函数也没变。 2、增加两个函数,取小数位数函数和结果处理(回归小数点)函数 3、与加法浮点高精度运算相比,这里改变较多的是结果处理函数,加法加完后,位数不减反增,而且最多增一位。减法会消失掉好多

高精度计算----减法运算

处理大数减法运算: 1、首先要判断被减数与减数哪个更大,再相应的带入减法函数去处理。具体的比较可以使用字符串的相关知识去比较。 2、相减要先对齐数组,依照减数的长度,执行相应的减法运算次数。 3、不需要借位相减的话,直接减去;需要的话,向前借一位,若前一位是0,则再前借(此时前一位的0变为10)。 测试程序效果如下:   以下代码包括相减函数,比较被减数减数函数,若有错误,请指出:

Python中的位运算-从入门到精通

你是否曾经好奇过计算机是如何在底层处理数据的?或者,你是否想知道为什么有些程序员总是津津乐道于位运算的强大?如果是,那么你来对地方了!今天,我们将深入探讨Python中的位运算,揭示它们的神奇之处,以及如何利用它们来优化你的代码。 目录 位运算:计算机的秘密语言为什么位运算重要? Python中的位运算操作符1. 按位与 (&)2. 按位或 (|)3. 按位异或 (^)4. 按位取反 (~

超强台风摩羯逼近!或成大陆史上最强登陆台风,防御措施需到位

超强台风摩羯逼近!或成大陆史上最强登陆台风,防御措施需到位 摩羯即将登录,各位兄弟姐妹注意安全!#大型纪录片#摩羯#台风 推荐阅读: 一夜蒸发2万亿!英伟达市值遭遇滑铁卢 《火速围观!黑神话悟空IP山西空心月饼,又一波抢购热潮即将来袭》 直击心灵!佤写不来情歌,却意外火爆全网,你听了没? 警告!明年6至9月假期空窗期,你的旅行计划何去何从? 独家揭秘!雷军豪赠《黑神话:悟空》给王腾,