位操作全解析:基础与实战技巧

源内容(英文)

位操作是一种在各种问题中以优化方式获得解决方案的技术。从竞技编程的角度来看,这种技术非常有效。它完全依赖于位运算符,这些运算符直接作用于二进制数或数的位,从而有助于加快实现速度。以下是使用的位运算符

  • 按位与(&)
  • 按位或(|)
  • 按位异或(^)
  • 按位非(!)

计算机程序中的所有数据在内部都以位的形式存储,即数字 0 和 1。

位的表示

在编程中,一个 n 位整数在内部存储为由 n 个位组成的二进制数。例如,C++ 的 int 类型是一个 32 位类型,这意味着每个 int 数由 32 个位组成。

int 数字 43 = 00000000000000000000000000101011

表示中的位从右到左进行索引。要将位表示 bk ···b2 b1 b0 转换为数字,我们可以使用公式

bk2k +…+ b222 + b121 + b020。

例如,1·25+1·23 +1·21 +1·20 = 43。

数的位表示可以是有符号的或无符号的。

通常使用有符号表示,这意味着可以表示负数和正数。

n 位的有符号变量可以包含介于 -2n-1 和 2n-1 – 1 之间的任何整数

C++ 中的 int 类型是有符号类型,因此 int 变量可以包含介于 -231 和 231 – 1 之间的任何整数。

有符号表示中的第一位是数字的符号,非负数为 0,负数为 1,剩余的 n−1 位包含数字的大小。

使用二补数,这意味着数字的相反数是通过首先反转数字中的所有位,然后将数字加一来计算的。

int 数字 −43 的位表示是 11111111111111111111111111010101

在无符号表示中,只能使用非负数,但值的上限更大。

n 位的无符号变量可以包含介于 0 和 2n −1 之间的任何整数。

在 C++ 中,unsigned int 变量可以包含介于 0 和 232 −1 之间的任何整数。

表示之间存在联系:

有符号数 −x 等于无符号数 2n − x。

例如,下面的伪代码片段显示有符号数

x = −43 等于无符号数 y = 232 −43:

!<a href="https://media.geeksforgeeks.org/wp-content/uploads/20230310205206/Screenshot20230310085038.png">imagethe pseudo-code snippet shows that the signed number

如果数字大于位表示的上限,数字将溢出。在有符号表示中,2n-1 – 1 之后的下一个数字是 -2n-1,而在无符号表示中,2n -1 之后的下一个数字是 0。例如,考虑下面的伪代码片段:

!<a href="https://media.geeksforgeeks.org/wp-content/uploads/20230310205419/Screenshot20230310085316.png">image

最初,x 的值是 231 −1。这是可以存储在 int 变量中的最大值,所以 231 −1 之后的下一个数字是 −231 。

这篇文章中了解更多关于位运算符的信息。以下是一些在编程中经常使用的常见位操作:

位操作:

下表说明了使用位运算符执行操作时的结果。这里 0s1s 分别表示 01 的序列。

运算符

操作

结果 —

— 异或

X ^ 0s

X 异或

X ^ 1s

~X 异或

X ^ X

0 与

X & 0s

0 与

X & 1s

X 与

X & X

X 或

X \

0s

X

X \

1s

1s

X \

X

X### 获取位:

此方法用于查找给定数字 N 的特定位置(比如 i)的位。其思想是找到给定数字和 2i 的按位与,可以表示为 (1 << i)。如果返回的值是 1,则第 i 位置的位被设置。否则,它未被设置。

下面是相同的伪代码:

C++


CODEBLOCK_cf9b3b4d

Java


CODEBLOCK_40f7b5cf

Python3


CODEBLOCK_80273c13

C#


CODEBLOCK_31a0694b

Javascript


CODEBLOCK_508b6542

时间复杂度: O(1)
辅助空间: O(1)

设置位:

此方法用于将给定数字 N 的第 i 个位置设置为 1。

方法:

为了设置第 i 个位置的位,我们需要对给定数字和 2i 执行按位或运算,这可以表示为 (1 << i)。

下面是相同的伪代码:

C++


CODEBLOCK_d1c9e657

Java


CODEBLOCK_d86303d8

Python3


CODEBLOCK_cc7fd06f

C#


CODEBLOCK_84e0f85b

Javascript


CODEBLOCK_33cda5d0

时间复杂度: O(1)
辅助空间: O(1)

清除位:

此方法用于清除给定数字 N 的第 i 个位置的位(即将其设置为 0)。

方法:

要清除第 i 个位置的位,我们需要对给定数字和 2i 的按位非执行按位与运算,这可以表示为 ~(1 << i)。

下面是相同的伪代码:

C++


CODEBLOCK_8a60e8cc

Java


CODEBLOCK_f2c49e20

Python3


CODEBLOCK_786293ce

C#


CODEBLOCK_0969bcbc

Javascript


CODEBLOCK_1e4e05db

时间复杂度: O(1)
辅助空间: O(1)

在编程中我们如何使用位操作?

计算整数的二进制表示中 1 的个数:

方法 1(朴素方法):

  • 初始化 count = 0
  • 当整数不为 0 时循环。
  • 如果 (n & 1) == 1,则执行 count++。
  • 右移 n 1 位。
  • 返回 count

时间复杂度:O(log n),其中 n 是给定的整数。

空间复杂度:O(1)

方法 2(Brian Kernighan 算法):

使用Brian Kernighan算法,可以消除迭代次数等于数字中置位次数的循环。基本上,这个算法跳过了 1 之间的 0。那么这个算法是如何工作的呢?其核心思想是,从数字 n 中减去 1 会翻转最右边的位(包括最右边的 1)并保留其他位不变。所以,如果我们执行 n & (n-1),它就会从二进制表示中去除最右边的 1。

让我们举个例子,n = 7 (111)2

(n – 1) = 6 (110)2

n & (n-1) = 4 (100)2

现在 n = 4

(n – 1) = 3 (011)2

n & (n-1) = 0 (000)2

现在 n = 0,循环结束。

在这里,我们迭代了 3 次,而在方法 1 中,我们必须迭代 7 次(直到 n 变为 0)。这节省了我们的计算时间。

时间复杂度:O(log n),其中 n 是给定的整数。

空间复杂度:O(1)

检查一个数字是不是 2 的幂:

一个数字 n 是 2 的幂,当且仅当 n 的二进制表示中正好有一个置位。

例如,4 = 100,8 = 1000,16 = 10000。

我们只需要检查 n 和 (n-1) 的按位与是否等于 0。

让我们再举个例子,n = 4,二进制 100

n – 1 = 3,二进制 011

n & (n-1) = 100 & 011 = 000

因为结果是 0,所以 4 是 2 的幂。

整数溢出:

当我们对整数进行算术运算时,结果是计算的数学值,即使这个值无法用给定数据类型的位数来表示。当无法表示时,它就被称为溢出。在整数运算中,溢出会导致忽略最高有效位,这会导致某些情况下的错误结果。

有符号整数溢出导致未定义的行为,这意味着编译器可以假定溢出永远不会发生,并对其进行优化。

无符号整数溢出导致整数按模 2^n 回绕,其中 n 是存储该整数的位数。

浮点数和双精度数:

计算机使用 IEEE 754 标准来表示浮点数。浮点数使用符号、指数和尾数来表示一个数字。

  • 符号:1 位
  • 指数:8 位(单精度)/ 11 位(双精度)
  • 尾数:23 位(单精度)/ 52 位(双精度)

位操作通常不直接应用于浮点数或双精度数。但是,你可以检查符号位(最高有效位)以确定数字是否为负。

此外,如果你知道表示的确切位模式,可以使用按位运算符来操作浮点数的位,但由于精度和舍入问题,这通常是不鼓励的。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/22710.html
点赞
0.00 平均评分 (0% 分数) - 0