《编程珍珠》第二版中集合的位向量实现

2024-02-14

在《Programming Pearls》第二版第 140 页上,Jon 提出了一种使用位向量实现集合的方法。

现在我们将转向两个最终结构,它们利用了我们的集合代表整数这一事实。位向量是第 1 栏的老朋友。以下是它们的私有数据和函数:

enum { BITSPERWORD = 32, SHIFT = 5, MASK = 0x1F };
int n, hi, *x;
void set(int i)  {        x[i>>SHIFT] |=  (1<<(i & MASK)); }
void clr(int i)  {        x[i>>SHIFT] &= ~(1<<(i & MASK)); }
int  test(int i) { return x[i>>SHIFT] &=  (1<<(i & MASK)); }

据我所知,用位向量表示整数集的中心思想(如第 1 列中所述)是,当且仅当整数 i 在该集合中时,第 i 位才会打开。

但我对以上三个函数所涉及的算法实在是一头雾水。而且书上也没有给出解释。

我只能得到那个i & MASK就是得到i的低5位,而i>>SHIFT就是向右移动 i 5 位。

有人会详细说明这些算法吗?位运算对我来说总是一个神话,:(


位域和你

我将使用一个简单的示例来解释基础知识。假设您有一个四位无符号整数:

[0][0][0][0] = 0

您可以通过将其转换为基数 2 来表示从 0 到 15 的任何数字。假设我们让右端最小:

[0][1][0][1] = 5

因此第一位加 1,第二位加 2,第三位加 4,第四位加 8。例如,这里是 8:

[1][0][0][0] = 8

So What?假设您想在应用程序中表示二进制状态 - 如果启用了某个选项,是否应该绘制某个元素等等。您可能不想为其中每一个使用整个整数 - 它会使用 32 位整数来存储一位信息。或者,以四位继续我们的示例:

[0][0][0][1] = 1 = ON
[0][0][0][0] = 0 = OFF //what a huge waste of space!

(当然,这个问题在现实生活中更加明显,因为 32 位整数如下所示:

[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0] = 0

答案是使用位字段。我们有一组属性(通常是相关的属性),我们将使用位操作打开和关闭它们。比如说,您可能在一个硬件上有 4 个不同的灯,您想要打开或关闭它们。

 3  2  1  0
[0][0][0][0] = 0

(为什么我们从 light 0 开始?我稍后会解释这一点。) 请注意,这是一个整数,并且存储为整数,但用于表示多个对象的多个状态。疯狂的!假设我们打开灯 2 和 1:

 3  2  1  0
[0][1][1][0] = 6

这里您应该注意的重要一点是:可能没有明显的理由说明为什么灯 2 和灯 1 亮应该等于 6,并且我们如何使用这种信息存储方案做任何事情可能并不明显。如果添加更多位,它看起来不会更明显:

 3  2  1  0
[1][1][1][0] = 0xE \\what?

我们为什么关心这个?对于 0 到 15 之间的每个数字,我们是否只有一个状态?如果没有一系列疯狂的 switch 语句,我们将如何管理这个状态?啊...

末日之光

因此,如果您以前使用过二进制算术,您可能会意识到左边的数字和右边的数字之间的关系当然是以 2 为基数。即:

1*(23) + 1*(22) + 1*(21) +0 *(20) = 0xE

因此,每个光都存在于方程每一项的指数中。如果灯亮,则其项旁边有一个 1;如果灯灭,则有一个 0。花点时间说服自己,0 到 15 之间只有一个整数对应于该编号方案中的每个状态。

位运算符

现在我们已经完成了这一步,让我们花点时间看看位移对这个设置中的整数做了什么。

[0][0][0][1] = 1

当您在整数中向左或向右移动位时,它实际上是向左和向右移动位。 (注:我100%不同意这种对负数的解释!有龙!)

1<<2 = 4
[0][1][0][0] = 4
4>>1 = 2
[0][0][1][0] = 2

当移位用多于一位表示的数字时,您会遇到类似的行为。另外,让自己相信 x>>0 或 x

这可能向任何不熟悉 Shift 运算符的人解释了它们的命名方案。

按位运算

这种二进制数字表示也可用于阐明整数上的按位运算符的运算。第一个数字中的每一位都与其同伴数字进行异或、与或或运算。花点时间浏览维基百科并熟悉这些布尔运算符的功能 - 我将解释它们如何在数字上起作用,但我不想详细地重复一般概念。

...

欢迎回来!让我们首先检查 OR (|) 运算符对存储在四位中的两个整数的影响。

 OR OPERATOR ON:
 [1][0][0][1] = 0x9
 [1][1][0][0] = 0xC
________________
 [1][1][0][1] = 0xD

艰难的!这与布尔 OR 运算符的真值表非常相似。请注意,每一列都会忽略相邻的列,而只是将第一位和第二位进行或运算的结果填充到结果列中。笔记also与 1 进行或运算的任何值在该特定列中均为 1。任何与零进行或运算的值都保持不变。

AND (&) 的表格很有趣,尽管有些颠倒:

 AND OPERATOR ON:
 [1][0][0][1] = 0x9
 [1][1][0][0] = 0xC
________________
 [1][0][0][0] = 0x8

在这种情况下,我们做同样的事情 - 我们对列中的每个位执行 AND 运算,并将结果放入该位中。没有列关心任何其他列。

关于这一点的重要教训,我邀请您使用上图来验证:任何与零进行“与”运算的内容都是零。另外,同样重要的是,与 1 进行“与”运算的数字不会发生任何变化。他们保持不变。

决赛桌 XOR 的行为我希望你们现在都发现是可预测的。

 XOR OPERATOR ON:
 [1][0][0][1] = 0x9
 [1][1][0][0] = 0xC
________________
 [0][1][0][1] = 0x5

每个位都与其列、yadda yadda 等进行异或。但仔细观察第一行和第二行。哪些位发生了变化? (一半。)哪些部分保持不变? (回答这个问题没有任何意义。)

当(且仅当)第二行中的位为 1 时,第一行中的位才会在结果中发生更改!

一个灯泡的例子!

现在我们有了一组有趣的工具,可以用来翻转各个位。让我们回到灯泡的例子,只关注第一个灯泡。

 0
[?] \\We don't know if it's one or zero while coding

我们知道有一个操作可以使该位始终等于 1——OR 1 运算符。

0|1 = 1
1|1 = 1

所以,忽略其余的灯泡,我们可以这样做

4_bit_lightbulb_integer |= 1;

并且确信我们除了将第一个灯泡打开之外什么也没做。

 3  2  1  0
[0][0][0][?] = 0 or 1? \\4_bit_lightbulb_integer
[0][0][0][1] = 1
________________
[0][0][0][1] = 0x1

同样,我们可以将数字与零相与。好吧——不完全是零——我们不想影响其他位的状态,所以我们将用 1 填充它们。

我将使用一元(单参数)运算符进行位否定。 ~ (NOT) 按位运算符翻转其参数中的所有位。 〜(0X1):

[0][0][0][1] = 0x1
________________
[1][1][1][0] = 0xE

我们将把它与下面的 AND 位结合使用。

让我们做 4_bit_lightbulb_integer & 0xE

 3  2  1  0
[0][1][0][?] = 4 or 5? \\4_bit_lightbulb_integer
[1][1][1][0] = 0xE
________________
[0][1][0][0] = 0x4

我们在右侧看到很多没有任何直接相关性的整数。如果您经常处理位字段,您应该习惯这一点。看左边。右边的位始终为零,其他位不变。我们可以关掉灯 0 并忽略其他一切!

最后,您可以使用 XOR 位选择性地翻转第一位!

 3  2  1  0
[0][1][0][?] = 4 or 5? \\4_bit_lightbulb_integer
[0][0][0][1] = 0x1
________________
[0][1][0][*] = 4 or 5?

我们实际上不知道 * 现在的值是多少 - 只是从什么翻转过来?曾是。

组合位移位和按位运算

关于这两个操作的有趣事实是,当结合在一起时,它们允许您操作选择性位。

[0][0][0][1] = 1 = 1<<0
[0][0][1][0] = 2 = 1<<1
[0][1][0][0] = 4 = 1<<2
[1][0][0][0] = 8 = 1<<3

唔。有趣的。我将在这里提到否定运算符 (~),因为它以类似的方式使用来生成位字段中的 AND 运算所需的位值。

[1][1][1][0] = 0xE = ~(1<<0)
[1][1][0][1] = 0xD = ~(1<<1)
[1][0][1][1] = 0xB = ~(1<<2)
[0][1][1][1] = 0X7 = ~(1<<3)

您是否看到移位值与移位位的相应灯泡位置之间存在有趣的关系?

规范的位移运算符

正如上面提到的,我们有一个有趣的通用方法,可以使用上面的移位器打开和关闭特定的灯。

为了打开灯泡,我们使用位移位在正确的位置生成 1,然后将其与当前灯泡位置进行或运算。假设我们要打开灯 3,忽略其他一切。我们需要进行位移位运算,或

 3  2  1  0
[?][?][?][?]  \\all we know about these values at compile time is where they are!

and 0x8

[1][0][0][0] = 0x8

这很容易,这要归功于位移!我们将选择灯的数量并切换值:

1<<3 = 0x8

进而:

4_bit_lightbulb_integer |= 0x8;

 3  2  1  0
[1][?][?][?]  \\the ? marks have not changed!

我们可以保证第三个灯泡的位设置为 1,并且其他任何位置都没有改变。

清除位的工作原理类似 - 我们将使用上面的否定位表来清除灯 2。

~(1<<2) = 0xB = [1][0][1][1]

4_bit_lightbulb_integer & 0xB:

 3  2  1  0
[?][?][?][?] 
[1][0][1][1]
____________
[?][0][?][?]

翻转位的异或方法与或方法的思想相同。

所以位交换的规范方法是这样的:

打开灯我:

4_bit_lightbulb_integer|=(1<<i)

关掉灯我:

4_bit_lightbulb_integer&=~(1<<i)

翻转灯 i:

4_bit_lightbulb_integer^=(1<<i)

等等,我该如何阅读这些内容?

为了检查一位,我们可以简单地将除我们关心的位之外的所有位清零。然后我们将检查结果值是否大于零,因为这是唯一可能非零的值,当且仅当它非零时,它才会使整个整数非零。例如,要检查位 2:

1

[0][1][0][0]

4_bit_lightbulb_integer:

[?][?][?][?]

1

[0][?][0][0]

还记得前面的例子中 的值吗?没有改变。还要记住,任何 AND 0 都是 0。因此,我们可以肯定地说,如果该值大于零,则位置 2 处的开关为真,并且灯泡为零。同样,如果该值关闭,则整个事物的价值将为零。

(您可以交替地将 4_bit_lightbulb_integer 的整个值移移 i 位,然后将其与 1 相与。我不记得是否有一个比另一个快,但我对此表示怀疑。)

所以规范检查函数:

检查位 i 是否打开:

if (4_bit_lightbulb_integer & 1<<i) {
\\do whatever

}

具体细节

现在我们已经有了一套完整的按位运算工具,我们可以看这里的具体示例。这基本上是相同的想法 - 除了更简洁和更强大的执行方式。我们来看看这个函数:

void set(int i)  {        x[i>>SHIFT] |=  (1<<(i & MASK)); }

从规范的实现中,我猜测这是试图将某些位设置为 1!让我们取一个整数,看看如果我将值 0x32(十进制 50)输入到其中会发生什么i:

x[0x32>>5] |= (1<<(0x32 & 0x1f))

好吧,那是一团糟..让我们剖析一下右边的这个操作。为了方便起见,假设还有 24 个不相关的零,因为它们都是 32 位整数。

...[0][0][0][1][1][1][1][1] = 0x1F
...[0][0][1][1][0][0][1][0] = 0x32
________________________
...[0][0][0][1][0][0][1][0] = 0x12

看起来一切都在顶部边界处被切断,1 变成了 0。这种技术称为位掩码。有趣的是,这里的边界将结果值限制在 0 到 31 之间……这正是 32 位整数的位数!

x[0x32>>5] |= (1

...[0][0][1][1][0][0][1][0] = 0x32

右移五位:

...[0][0][0][0][0][0][0][1] = 0x01

Note that this transformation exactly destroyed all information from the first part of the function- we have 32-5 = 27 remaining bits which could be nonzero. This indicates which of 227 integers in the array of integers are selected. So the simplified equation is now:

x[1] |= (1<<0x12)

这看起来就像规范的位设置操作!我们刚刚选择了

因此,我们的想法是使用前 27 位来选择要移位的整数,最后 5 位指示要移位该整数中 32 位中的哪一位。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《编程珍珠》第二版中集合的位向量实现 的相关文章

  • 通过排列四个给定数字找到最大可能时间 HH:MM

    我最近为了工作晋升而参加了编码测试 这是我真正遇到的任务之一 我想知道什么是最好的方法来做到这一点 我使用了大量的 if 和 if else 这不是最干净的解决方案 但完成了工作 我被问到的问题是 将 4 个数字格式化为 24 小时时间 0
  • 比 BMH (Boyer–Moore–Horspool) 更快的算法

    您会使用哪种算法来搜索短文本中的短子字符串 简而言之 我的意思是子字符串有 5 10 个字符 字符串有 255 个字符 我正在考虑根据输入数据长度选择算法 哪种算法对于较长的输入更好 Try Turbo BM http www igm un
  • 2D形状识别与解析算法

    我正在寻找一种算法 用于从给定的一组 x y 点检测简单形状 如矩形 三角形 正方形和圆形 我还在寻找一种方法 一旦检测到 将路径转换为更干净的形状 我已经查遍了互联网 但没有找到任何 简单 的方法 几乎所有这些对于我的简单实现来说都是高级
  • 寻找簇的中心

    我有以下问题 进行抽象以找出关键问题 我有 10 个点 每个点与其他点有一定距离 我想要 能够找到簇的中心 即与其他点的成对距离最小的点 令 p j p k 表示点 j 和 k 之间的成对距离p i 是簇的中心点 iff p i s t m
  • 求一根棒可以切割的最大片数

    这是完整的问题陈述 给定一根长度为n的绳子 你需要找到最大的绳子数你可以让每一段的长度都在集合 a b c 中给定三个值a b c 我知道可以通过动态规划来实现最优解 但是 我还没有学过这个主题 我需要递归地解决这个问题 对于递归 主要的事
  • 查找重叠事件/时间的算法

    在处理自定义日历时 我不知道如何找到与任何其他时间段重叠的时间段 时段从 0 点至 720 点 上午 9 点至晚上 9 点 每个像素代表一分钟 var events id 1 start 0 end 40 an event from 9 0
  • 带有元数据的 scipy kdtree

    我目前正在寻找一种方法来构建几个 kd 树以快速查询一些 n 维数据 但是 我对 scipy KD 树算法有一些问题 我的数据包括id gt data somedata coordinate x y 我希望能够基于坐标和 k 最近邻居的 i
  • 对列表中的相邻元素进行分组

    假设我想编写一个函数来执行此操作 输入 1 1 3 3 4 2 2 5 6 6 输出 1 1 3 3 4 2 2 5 6 6 它将相同的相邻元素分组 这个方法的名称应该是什么 此操作有标准名称吗 In 1 1 3 3 4 2 2 5 6 6
  • 如何确定字符串的最小公约数?

    我在面试时被问到以下问题 并被它难住了 我遇到的部分问题是要下定决心要解决什么问题 起初我并不认为这个问题在内部是一致的 但后来我意识到它要求你解决两个不同的问题 第一个任务是弄清楚一个字符串是否包含另一个字符串的倍数 但第二个任务是在两个
  • 随机排列

    我无法找到一种随机洗牌元素的好方法std vector经过一些操作后 恢复原来的顺序 我知道这应该是一个相当简单的算法 但我想我太累了 由于我被迫使用自定义随机数生成器类 我想我不能使用std random shuffle 无论如何这没有帮
  • 查找两个大小为 n 的数组中第 n 大数的算法

    我有这个问题 给定两个大小为 n 的排序列表 存储在数组中 找到 O log n 计算并集中第 n 大元素的算法 两个列表 我可以看到这里可能有一个技巧 因为它需要第 n 个最大的元素 并且数组的大小也是 n 但我不知道它是什么 我在想我可
  • 将 n 个可变高度图像拟合为 3 个(相似长度)列布局

    我正在寻找类似于的 3 列布局piccsy com http piccsy com 给定许多宽度相同但高度不同的图像 有什么算法可以对它们进行排序以使列长度的差异最小 最好使用 Python 或 JavaScript 非常感谢您提前的帮助
  • 大小为 n 的数组,其中一个元素 n/2 次

    给定一个由 n 个整数组成的数组 其中一个元素出现超过 n 2 次 我们需要在线性时间和恒定的额外空间中找到该元素 YAAQ 又一个数组问题 我有一种偷偷的怀疑 这类似于 在 C 中 We don t need an array publi
  • Python 将字符串组合成尽可能短的字符串?

    如果我有一个字符串列表 我想将它们组合成一个具有重叠字符的字符串 如果没有剩余的重叠字符串 请将其添加到末尾 这是一个过于简化的版本 input one two output twone 我正在寻找一种方法来对输入列表中的任意数量的字符串执
  • std::__gcd 和 std::gcd 有什么区别?

    Many https www geeksforgeeks org stdgcd c inbuilt function finding gcd websites https codeforces com submissions Madiyar
  • 具有最小刻度的图表的漂亮标签算法

    我需要手动计算图表的刻度标签和刻度范围 我知道漂亮刻度的 标准 算法 参见 我也知道这个Java实现 http erison blogspot nl 2011 07 algorithm for optimal scaling on char
  • 地形/山地算法未按预期工作

    我想使用一个非常基本的原理创建一个上面有山的地形 如以下高度图所示 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0
  • 线性问题和非线性问题之间的区别?点积和核技巧的本质

    核技巧将非线性问题映射为线性问题 我的问题是 1 线性问题和非线性问题的主要区别是什么 这两类问题的差异背后的直觉是什么 核技巧如何帮助在非线性问题上使用线性分类器 2 为什么点积在这两种情况下如此重要 Thanks 当人们说到分类问题的线
  • 如何发现“贪婪”算法?

    我正在读一本关于 贪婪 算法 但我很难发现它们解决真正的 顶级程序员 问题 If I know给定的问题可以用 贪婪 算法来解决 因此编写解决方案非常容易 然而 如果我没有被告知这个问题是 贪婪的 我就无法发现它 用 贪婪 算法解决的问题有
  • 在c#中遍历对象树

    我有一棵由多个对象组成的树 其中每个对象都有一个名称 string id int 以及可能是同一类型的子数组 如何遍历整个树并打印出所有 id 和名称 我是编程新手 坦率地说 我很难理解这个问题 因为我不知道有多少个级别 现在我正在使用fo

随机推荐

  • 使用 STOMP 连接 RabbitMQ 时如何重播丢失的消息?

    我有一个 iOS 应用程序 它使用STOMP客户端 https github com juretta objc stomp交谈RabbitMQ https www rabbitmq com 应用程序在启动期间加载大量状态 然后通过接收 ST
  • 在 ListView 中显示照片,如何让适配器在用户可以看到列表之前预加载?

    我需要在列表视图中显示许多照片 这些照片是从列表视图中的网址中提取的 不幸的是 下载时间存在一些差异 实际下载是在 ListView 的适配器中完成的 这些下载是 runOnUIThread 是的 我知道这是可怕的设计 因此 发生的情况是下
  • 如何将 Azure Powershell 模块添加到 Visual Code Intellisense

    有谁知道如何在可视化代码中为Azure模块添加智能感知 我最近安装了 Azure Powershell 模块 并想使用可视代码编写一些 powershell 脚本 但编辑器没有为我提供 az 函数的任何智能感知 例如 Get AzResou
  • 如何在 MATLAB 中绘制具有相同色阶的不同曲面?

    我试图表示几个比例略有不同的曲面图 每个曲面图都绘制在单独的子图和 或图中 现在 我正在使用默认的颜色映射 它会自动将颜色映射的整个范围缩放到我的图形 即我的表面的最大值始终为红色 在 jet 颜色模式下 无论该最大值的大小如何 我希望颜色
  • 为什么 left 在 x86 汇编中执行“mov esp,ebp”?

    据说 leave指令与以下相同 mov esp ebp pop ebp 但什么是mov esp ebp来这里是为了 这对我来说似乎无效 mov esp ebp将堆栈指针设置为基帧地址 有效地释放整个帧 不要忘记这是英特尔语法 目的地是第一位
  • 如果在 AsyncTaskLoader 运行期间发生方向更改,则不会调用 LoaderCallbacks.onLoadFinished

    使用 android support v4 jar 和 FragmentActivity 此时没有片段 我有一个 AsyncTaskLoader 我开始加载它 然后在后台线程仍在运行时更改方向 在我的日志中 我看到对后台请求的响应 响应完成
  • 货物、工作空间和临时本地依赖

    我在一个货物工作区中有两个项目 my project 和 my inner project 它们都依赖于 gfx 以及 gfx core 和 gfx device gl 我在 gfx device core 中发现了一个错误 所以我在本地分
  • 为 Woocommerce 中的特定用户角色应用折扣

    我有一个 woocommerce 商店 有 3 个用户角色 我想仅为用户角色 公司 提供购物车总额 10 的折扣 I found 基于 Woocommerce 中的用户角色和付款方式的百分比折扣 https stackoverflow co
  • Direct3D 10 是否有 COM 暴露

    先生们 尊敬的女士们 我在 Code Project 的 COM 论坛上发布了这个问题 并得到了一个傲慢的回复 希望对您有所帮助 我看到 Microsoft 有一个用于 Direct3D 9 的 COM 库 其 GUID 为 81BDCBC
  • 如何在R中创建列的md5哈希值?

    我有一个数据框 ID VID 1 xyz 0001 我想更换VIDmd5 哈希为VID列值 我该如何在 R 中做到这一点 我在看digest包但不知道如何将其放入 R 代码中 Thanks Package digest绝对适合这个任务 所以
  • Angular-jwt 如何在没有秘密的情况下解码我的 JWT?

    Auth0 团队创建了一个名为 angular jwt 的东西 它有一个 jwtHelper 类 这个东西成功解码了本地 JWT 而无需我在服务器上使用的秘密 这怎么发生的 如果它们不安全 那么使用秘密来签名 加密它们有什么意义呢 服务器上
  • HTML5

    我正在为客户开发一个网站 他们坚持使用 HTML5 的视频标签作为某些视频内容的交付方法 我目前在以下方面的帮助下已经启动并运行了它http videojs com http videojs com 处理 Internet Explorer
  • Android:如何将活动声明为主且可搜索?

    我希望我的主要活动也可以搜索 但是当我将 manifest xml 更改为
  • 如何在 macOS 上检测远程音频按钮?

    文章中处理外部玩家事件通知 https developer apple com documentation mediaplayer handling external player events notifications language
  • WPF DataGrid - 新条目的行不可见

    问题是 DataGrid 中的空白行没有出现 因此用户无法添加数据 这是代码 System Collections ObjectModel ObservableCollection
  • Android WebView 中的 HTML5 视频不一致

    当在 Android WebView 中的 HTML5 页面上显示 mp4 视频时 从远程 URL 检索文件时 视频和音频都会正确播放 当尝试从设备的 mnt sdcard 路径中播放相同的媒体文件时 仅播放媒体文件的音频部分 对此有什么想
  • 将文件加载到向量

    我想将文本文件的内容加载到vector
  • MVC 模型状态验证在列表框中失败

    我有一个简单的模型 它使用多选列表框来实现多对多 EF 关系 On my Create行动 我收到错误 从类型 System String 到类型 MyProject Models Location 的参数转换失败 因为没有类型转换器可以在
  • 实体类型和实体集之间的区别? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 什么是属性 实体 实体类型和实体集有什么区别 请举例说明其中的区别 STUDENT 身份证号码 姓名 年龄 1 公羊 122 萨姆 1
  • 《编程珍珠》第二版中集合的位向量实现

    在 Programming Pearls 第二版第 140 页上 Jon 提出了一种使用位向量实现集合的方法 现在我们将转向两个最终结构 它们利用了我们的集合代表整数这一事实 位向量是第 1 栏的老朋友 以下是它们的私有数据和函数 enum