被大学教师忽略的运算符”左移位”

灵感来源

最近在刷力扣每日题的时候遇到了这么一道题

官方给出的题解

var prefixesDivBy5 = function(nums) {
    const answer = [];
    let prefix = 0;
    const length = nums.length;
    for (let i = 0; i < length; i++) {
        prefix = ((prefix << 1) + nums[i]) % 5;
        answer.push(prefix === 0);
    }
    return answer;
};

从上面代码可以得出不断的循环更新prefix变量的结果,一但满足条件就都丢给answer数组

经过Ai以及搜索,感叹计算机深层逻辑的伟大

这是一个非常核心的计算机运算概念

核心概念

左移位 是一种位运算,它将一个数的所有二进制位整体向左移动指定的位数。低位补0,高位溢出被丢弃。

基本规则

对于一个二进制数 a,左移 n 位(记作 a << n):

  1. a 的所有位向左移动 n 位。
  2. 最右边的 n 个空位用 0 填充。
  3. 最左边溢出的 n 位被丢弃。

示例(以8位二进制为例)

示例1: 十进制 5 左移 1 位

数字 5 的二进制:  0000 0101
左移 1 位:       0000 1010  (最左的0溢出丢弃,最右补0)
结果 0000 1010 的十进制是: 10

所以,5 << 1 = 10

示例2: 十进制 3 左移 2 位

数字 3 的二进制:  0000 0011
左移 2 位:       0000 1100  (最左的00溢出丢弃,最右补00)
结果 0000 1100 的十进制是: 12

所以,3 << 2 = 12

示例3: 十进制 65 左移 2 位(会发生溢出)

数字 65 的二进制:  0100 0001
左移 2 位:        0000 0100  (高位的`01`溢出丢弃,最右补`00`)
结果 0000 0100 的十进制是: 4

可以看到,高位有效位被丢弃,结果发生了改变。

一个重要特性(对于无符号数和正有符号数)

在大多数编程语言中,对于一个非负整数(无符号整数或正的有符号整数),左移 n 位等效于乘以 2 的 n 次方

公式: a << na * (2^n)

从上面的例子可以验证:

  • 5 << 1 = 10 等于 5 * 2
  • 3 << 2 = 12 等于 3 * 4

为什么是“近似等于”?
因为当结果超出了该数据类型能表示的范围(即发生溢出)时,这个等式就不成立了。例如,在8位整数中,130 << 1 的结果是 4(溢出),而不是 260

在不同编程语言中的使用

语法几乎相同,都用 << 运算符。

C++/Java/JavaScript/C#/Go 等:

int x = 5;
int result = x << 2; // result = 20

Python:

x = 5
result = x << 2  # result = 20

注意事项

  1. 有符号数的左移:对于有符号数(如 int),左移操作在C/C++等语言中是未定义行为实现定义的,如果移动导致符号位(最高位)改变。在Java等语言中,<< 是算术左移,直接丢弃高位,低位补0,不考虑符号。通常建议对无符号类型进行位操作以避免歧义。
  2. 移动位数不能大于等于位数:如果移动的位数 n 大于或等于数据类型的总位数(例如,对32位 int 移动32位或更多),在C/C++中是未定义行为。在许多语言中,实际移动位数是 n mod 位数(如Java)。
  3. 效率:左移指令在CPU中执行速度非常快。在注重性能的代码中,a << 3 通常比 a * 8 更受青睐(但现代编译器通常会自动进行这种优化)。

总结

特性说明
运算符<<
操作将二进制位整体左移
空位填充低位补 0
溢出高位直接丢弃
数学意义对于非负数,相当于乘以 2^n(不溢出时)
主要用途快速乘以2的幂次、位标志设置、底层硬件操作、加密算法、优化计算等。

简单来说,左移位就是让二进制数“变大”的一种快速位操作,本质是向更高位(2的更高次幂)移动。

最后得出结论:

本题的prefix<<就是不断的在计算乘以2的值,即:

二进制(10)=十进制(2),二进制(100)=十进制(4),二进制(1000)=十进制(8)

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容