* 我们给定两个数字 A 和 B。我们的任务是计算将 A 转换为 B 所需翻转的位数。
*
* 解释:
*
* A = 01010 B = 10100
* 如我们所见,A 中需要翻转的位是 01010。
* 如果我们翻转这些位,我们得到 11110,这就是 B。
namespace bit_manipulation {
namespace count_bits_flip {
std::uint64_t countBitsFlip(
std::int64_t A,
std::int64_t B) { // int64_t is preferred over int so that
// no Overflow can be there.
int count =
0; // "count" variable is used to count number of bits flip of the
// number A to form B in binary representation of number 'n'
A = A ^ B;
while (A) {
A = A & (A - 1);
count++;
}
return count;
}
} // namespace count_bits_flip
} // namespace bit_manipulation
下面是对这段代码的详细解释:
名称空间(Namespace)
-
count_bits_flip
:这是一个名称空间,用于组织代码,避免与其他代码的函数或变量名称冲突。
函数定义
-
countBitsFlip
:这是一个函数,用于计算将整数A
转换成整数B
所需的比特翻转次数。
参数
-
A
:这是一个std::int64_t
类型的参数,表示要翻转的原始整数。 -
B
:这也是一个std::int64_t
类型的参数,表示目标整数。
返回值
-
函数返回一个
std::uint64_t
类型的值,表示将A
转换成B
所需的比特翻转次数。
函数内部实现
-
异或操作:
-
A = A ^ B;
:这行代码对A
和B
进行异或操作。异或操作会在A
和B
的二进制表示中,将不同的位标记为 1,相同的位标记为 0。这样,异或后的结果中的每个 1 都表示需要翻转的位。
-
-
计数变量初始化:
-
int count = 0;
:这行代码初始化一个计数变量count
,用于统计需要翻转的位数。
-
-
循环统计 1 的个数:
-
while (A)
:这个循环会一直执行,直到A
变为 0。 -
A = A & (A - 1);
:这行代码清除A
的二进制表示中最右边的 1。具体来说,A - 1
会将最右边的 1 变为 0,并将右边的所有 0 变为 1。然后,对A
和A - 1
进行按位与操作,就将最右边的 1 清除了 。 -
count++;
:每次循环,计数变量count
增加 1,表示找到一个需要翻转的位。
-
-
返回结果:
-
return count;
:最后,函数返回计数变量count
,即所需的比特翻转次数。
-
整体思路
-
该函数通过异或操作找出
A
和B
二进制表示中不同的位,然后统计这些不同位的个数,从而得到所需的比特翻转次数。 -
使用
std::int64_t
类型是为了确保函数可以处理较大的整数范围,避免整数溢出问题。
这个方法的优点是高效且简洁,利用位运算的特性来快速统计所需的翻转次数,而不需要逐位检查。
下面通过举例来详细解释这句代码 A = A & (A - 1)
是如何清除 A
的二进制表示中最右边的 1 的。
假设 A = 6
二进制表示为 110
。
-
A - 1:
-
将
A
减去 1,即6 - 1 = 5
。 -
5 的二进制表示为
101
。
-
-
A & (A - 1):
-
将
A
和A - 1
进行按位与操作。 -
按位与操作的规则是:只有当两个位都为 1 时,结果位才是 1,否则为 0。
-
对于
A = 6
(二进制110
)和A - 1 = 5
(二进制101
),按位与操作的结果为100
(十进制的 4)。
-
通过这个操作,最右边的 1(即第二位的 1)被清除,变成了 0。
为什么这个操作有效?
-
A - 1 的原理:
-
当你对一个二进制数减去 1 时,它会将最右边的 1 变为 0,并将其右边的所有 0 变为 1。
-
例如:
-
如果
A
是1000
(8),那么A - 1
是0111
(7)。 -
如果
A
是1010
(10),那么A - 1
是1001
(9)。 -
如果
A
是1111
(15),那么A - 1
是1110
(14)。
-
-
-
按位与操作的作用:
-
当我们将
A
和A - 1
进行按位与操作时,最右边的 1 会被清除,因为A - 1
中的这个位置是 0,而其他位置保持不变。 -
这个操作实际上是从右到左找到第一个 1,并将其变为 0,同时将其右边的所有位变为 1,然后按位与操作将这些右边的 1 变为 0。
-
表格
A | 二进制表示(A) | A - 1 | 二进制表示(A - 1) | A & (A - 1) |
---|---|---|---|---|
6 | 110 | 5 | 101 | 100 |
5 | 101 | 4 | 100 | 100 |
7 | 111 | 6 | 110 | 110 |
8 | 1000 | 7 | 0111 | 0000 |
9 | 1001 | 8 | 1000 | 1000 |
10 | 1010 | 9 | 1001 | 1000 |
通过这些例子,可以看到每次执行 A & (A - 1)
操作后,最右边的 1 都会被清除。
这个操作非常高效,因为它避免了逐位检查,而是直接定位到最右边的 1 并清除它。这种方法在统计二进制数中 1 的个数或者类似的位操作场景中非常有用。